Why FireMonkey is so fundamentally wrong in every aspect of it’s being

A short time ago I had a harsh twitter argument with Nick Hodges (@NickHodges) about the FireMonkey framework in Delphi XE4 (you may know that I had the start of my professional career with Delphi and started as a speaker at Delphi conferences).

It all started with the definition of 'native' or - even worse - 'true native', but let's start at the beginning. I know the audience of my blog is mostly .NET focused, so let me get you all set with the required background information.

FireMonkey

So, let's start what FireMonkey is - or aims to be. FireMonkey is a application development framework (or platform, as Embarcadero likes to call it) and contains components that should enable the developer to build cross-applications with a singe code base for Windows, Mac OS X, iOS and soon to come Android. FireMonkey is written for Delphi and can also be used from C++ within Embarcaderos C++ Builder.

So the main idea is, that you design your forms with FireMonkey components and controls, double-click on buttons to add your business logic like Delphi developers did this for the last decades and then be able to compile the application for Windows, for Mac OS X and for iOS without changing it anymore.

And indeed, this works technically...

Architecture

...but this is also already the point where it starts getting wrong.

FireMonkey, by it's RAD approach, encourages the developer to click his user interface together, double-click on controls and put all the logic onto the form - where it doesn't belong. I'm not going to argue with anybody about decoupling, testability of code and all the other Clean Code aspects and concepts. A good developer should have the inner urge to produce code at a certain minimum quality level and putting everything on the form is nothing that helps here. So, the basic concept that FireMonkey encourages is wrong.

New and not-so-advanced developers tend to adopt this bad style and start running into a direction that will end up in fatality. Good developers instead will start by building up a good architecture for their application. Most probably working with tests, that makes the usage of some DI container a no-brainer. This most probably also lead to a good decoupled architecture on the application frontend, perhaps introducing some MVC concept for their GUI. Only this enables them to take a good approach to real and thoughtful cross platform development, but more on that in a minute.

Cross-Platform

Let's talk about cross platform development in a general way, before we go back to FireMonkey.

Every platform has its specifics. And a user - that is, in fact, our customer we want to sell our application too - chooses his platform for some reason. There are multiple approaches to make the user happy, and the most simple thing is, to integrate the app seamlessly into the environment (platform) the user chose to please him.

Let's talk a little about UX. I'm thinking about the overall user experience with your application here, not only the look & feel of the GUI. It's the whole full package including a good guidance through the workflow, helping the user to not enter crap into your app, assisting him to solve problems when he does, make everything accessible for everyone, especially impaired users, and of course also response times and stuff. As said, the whole package.

All platform vendors have thought about how their platform / devices should behave, how software should behave on the platform and what they expect from an application. They offer UX guidelines that describe what fits into the environment and how applications can fit seamlessly into the platform, providing an overall exquisite user experience to the guys you want money from.

Comparing just Apples (iOS and the Mac) and Googles (Android), which are the current relevant platforms for FireMonkey besides Desktop-Windows, UX guidelines shows you how different the platforms are. They are fundamentally different in how the control flow in applications is expected from the user. Leave alone Windows RT for tables and Windows Phone, which have a radical new approach to interacting with applications. But since Windows RT and Windows Phone are not (yet ?) supported, we don't need to get into those details right now. Just so far right now: Delphi is marketed as the best/fastest/most productive dev tool for Windows. Why can't you target Windows RT? Or write Windows Store applications for Windows 8 with it? Well, thats another topic. But still taking into account, that the main target audience for FireMonkey are Delphi (and as such mainly Windows-) developers, this leads into a fatal direction:

FireMonkey encourages the following: The Windows-Developer designs his FireMonkey form for Mobile Devices just as he would design a Windows-Application form. This for Windows designed UX is ported in a one-size-fits-all attempt onto the Mac (not so extremely terrible bad), but also to iOS and later Android (overly extremely bad).

Why is this bad? Because the user chose his platform with something in his mind. This something is the overall user experience with the device and of course with the applications he gets from the store within the platform itself. It's a closed ecosystem for his needs. He expects his iPhone/iPad-Applications to come in his loved iOS style or he expects his Android-Applications to come in an Android-Style. So again, we need to please our customers because they are the ones buying our applications and giving us their money. So how can we make them happy? Give them, what they expect.

Users expectations, and a sub-plot

That can be done only in one way: To embrace the platform and behave like a good citizen on that platform.
Developing a 'native' application is not the only way, but whatever technology you choose, the result should still integrate seamlessly into the environment.

A good example is Exfm. They have a music sharing service, and used to publish a 'native' iOS application. Native as in written in Objective-C with XCode. It had a 4-star rating. Still they did a rewrite of the app - with HTML5 and JavaScript - based on PhoneGap. During the rewrite and despite the fact that they were actually programming a web application that runs in a UIWebView browser, they incredibly focused on iOS detail behaviour like the scroll bounce thingy or the possibility to scroll-bounce elements that don't need to be scrolled because they are not larger than the area displaying them. They even mimicked the iOS behaviour that you can tap and hold on a button, move away from it, slide over the button again and lift your finger to trigger it. They did that with HTML 5.

They did that for one reason: To behave like a good citizen on iOS. To please their users.

And now they have a HTML application that you can't tell apart from a native application that uses the native UI controls of iOS. The new app now got a 4.5 star rating and more downloads than ever. Here's an article with a lot of more detail information about the little iOS details that make the app feel 'right' on iOS.

Now let's get back to FireMonkey.

Einheitsbrei

I'm trying to introduce a new word to my english speaking friends: 'Einheitsbrei'. You already use some german words like Zeitgeist and Kindergarten, and now it's time for 'Einheitsbrei'.

Einheitsbrei is a word that could be translated with 'boring standard mash'. Is used deprecative and describes things that are boring, common, and don't have any specific characteristic or outstanding elements.

FireMonkey apps are Einheitsbrei. They are sub-standard on every platform and don't take into account the little, loved by users, elements of the platform they are running on. And in some cases they don't even get the basic things right.

Architecture, the second: Doing it better

In the first architecture section I described that a good application architecture very probably involves some sort of DI and MVC on the GUI part.

Nick said on twitter:

So, let's take this for granted. When I can call 'any API I want' with Delphi for iOS, then I would have full access to all native UI controls on iOS. When I already have MVC in my application, then there is nothing that would hinder me as a developer to use the DI container to instantiate a view for iOS that is using the iOS native UI controls, and to instantiate a view for Android that makes use of the native Android UI controls there.

With just a little bit more effort on the views using the platform APIs, bypassing FireMonkeys UI controls, your app could behave like a good citizen on the very specific platform my customer chose for himself for a reason. Remember: that customer guy is the guy I need to make happy because I want him to give me his money.

Conclusion

Yes, it's more effort. Yes, it will take longer. Yes, it will require you to learn about the platforms UX design guidelines and about the platforms native UI controls. But it's worth it. Like extfm, who re-wrote an existing app with the goal to their iOS users more happy and another one that makes their Android users happy.

FireMonkey instead encourages you to produce Einheitsbrei. And this is just so wrong. You will find out, when you don't get the ratings required to have enough sales for your app. Users are cruel. They buy your app, and rate it down when they don't like it. And they tell other users to not buy your app, when they are not happy with it. They will, however, rate your app up and tell others to also buy when they really like it. But just when the app's buttons are so good, that they want to lick them.

Your app needs to be outstanding, of high quality and provide a well designed user experience to be successful and to be able to compete against other applications. Einheitsbrei doesn't sell. And FireMonkey, by design, encourages Einheitsbrei. You won't do yourself a favor by using it.

This is my opinion on why FireMonkey is just so wrong.

Setting up my infrastructure – Part 8: A little bit more evaluation

After a little break I'm back again with the next findings in my little pet project.

First of all I wanted to check out the build servers before diving into the bug trackers. So I set up the first assembly for my project, with just one class and a unit test for it. As I already mentioned in my requirements it is a .NET project and I want to have at least the very basics (building, unit testing etc.) covered.

My test case for this was very easy:
Set up a project in the build server, have it check out the sources of the project and let it build.
After building is okay, add the configuration for unit tests and let the test run.
Check if it builds when checking in new code, and check how it reacts when either the build breaks or unit tests fail.

So, now this is, what I encountered with the systems:

Bamboo

Without digging too deep into the documentation (Documentation? Yes, this is the url. Check.), I set up a build plan in Bamboo to build my project. I set up a first stage that should check out the project from source control and build it.

Well, the setup was done quickly, but the build would not start. What I did was the following: I configured two local agents, both are capable of running the build. The build plan had a single stage "Build" with two jobs: Checkout and call msbuild with my solution file.

When I manually selected 'Run' it was queued and 'waiting to be build' forever. When clicking onto it, and selecting the only stage with the hourglass besides it from the sidebar, it told me this status: "Status: Job has not yet been queued. Waiting for prior stages to complete.".

This was where I went like WTF? What prior stages? This was the only existing stage in the only existing plan.
After spending several hours with this situation I decided to create a User account on my Bamboo evaluation installation for someone to support me and opened a support ticket at Atlassian.

A few hours later the support logged in - and just saw a failed build that started like 20 minutes before.
After that the server behaved like normal: A new checkin resulted in an almost immediate new build like I would have expected.

So, why did the build fail? I used a Git repository on BitBucket for the tests, and configured the version control settings to check out from Git. I though that was the obvious way to do it. I was wrong, as Bamboo would not check out the sources. As I later found out, since the Git (hint! hint!) repo is hosted on BitBucket, I needed to select 'BitBucket' instead of Git from the repository type selection to be able to use it. My dear. After that it worked.

So, after this little problem I went on to configure the unit tests. I added a new stage for the tests to the build plan and configured a new job that would call MsTest and run the tests.

That job failed on every approach to run it. It always told me that the assembly was not found.
To make a long (several hours over a few days!) story short: Obviously the different stages in a build plan are executed on the same agent, but in different working directories.

At the time of this evaluation there wasn't anything hinting to that in the Atlassian docs. By now, there is a small article explaining that you need to configure Artifacts to bring build results from one stage into another, but without further explaining how to set up the artifacts in detail.

Back then, I tried to figure out the different directories but that required a lot of changes to my build scripts that wouldn't work on the dev machines after that, and so I put the MsTest job in the build stage to have it execute.

Guess what? After changing the build stage it wouldn't start again with the same delayed until forever problem I already had until I waited several hours when it suddenly executed.

After all, my experience with Bamboo wasn't really turning me on.

TeamCity

I already mentioned that we use TeamCity at my work place. So setting up the initial project was a bit new to me, but I knew what the settings were and where I had to tune a little bit. So the initial setup of my project with two build steps build and test was done in a few minutes.

Of course everything worked from the very instane I clicked on run.
Then I went on to activate code coverage reports for the test build step by simply activating dotCover from a little drop down in the test build step. After the next build I had a complete test coverage report on TeamCity.

Conclusion

I struggled a lot with Bamboo. It has a steep learning curve and the documentation is.. well, let's simply call it it could be better. A lot. The Atlassian tools in general, and Bamboo is no exception, are obviously powerful, but you have to spend a LOT of time with the system to get it running like you expect it to - and I don't want to spend too much time fiddling around with my toolset.

TeamCity on the other hand is streamlined and guides you through the process of configuring your builds. Everything I needed was set up in a matter of minutes and everything worked directly as expected from the very beginning.

Actually, at this point it was clear for me to use the JetBrains tools and not Atlassians, but I still had a quick look at Jira and YouTrack, which I want to describe in a dedicated blog post.

See the other parts in this series:

[contentblock id=infrastructurelinks]