Picking a language is not about what is fun to write this month. Someone has to keep the thing alive for years: run it, patch it, add to it long after the launch party. So we choose for the boring part, the long tail of a product’s life, not the first week of it.
Over time we settled on three tools and stopped arguing: Go, Flutter, and Rust. Here is what each one is actually for, in plain words.
Go: the backend and the glue
Go is our default for backend services and the small tools that keep a product running.
What we actually like about it:
- Almost no dependencies. Most of what a service needs is already in the box. Less borrowed code means fewer security surprises and fewer 2 a.m. breakages.
- One file to ship. A Go service builds into a single program. Nothing extra to install on the server, and it behaves the same in a container or on a plain box.
- Anyone can read it. A new developer is useful in days, not weeks. You are not held hostage by the one person who understands the codebase.
- Tests come for free. Checking that an update did not break something old is cheap, so we actually do it instead of promising to do it later.
Add that up and you get software that is simply cheaper to own. Not exciting. Just cheaper, every year.
Flutter: one app, every screen
If it has a screen, we build it in Flutter. One codebase runs on phone, desktop, and the web.
Why that matters to you, not just to us:
- You pay once, not three times. One team and one codebase instead of separate apps that slowly drift apart.
- Everyone gets the update together. No platform stuck a version behind while users complain.
- It is quick to change. We tweak something and see it almost immediately, so polishing the product is fast instead of a slow guessing game.
- It is not just forms and buttons. We have shipped real games with it, like OxiType and Neon Grid Sudoku. If it can run a smooth game, your app is not going to be the hard part.
Rust: only when it earns its place
Rust is the exception, not the rule. Most products never need it, and we do not add it to look clever. It shows up for one honest reason: some part of the work has to be genuinely fast.
When that need is real, Rust pays for itself:
- It is fast, and it stays fast under load. Predictable, not just quick on a good day.
- It is strict, almost annoyingly so. A whole class of bugs never makes it to production, which means fewer crashes and fewer late night phone calls.
We use it in exactly two ways, and never mixed together:
- As a backend on its own, when the entire job of a service is a heavy, performance critical workload.
- As a cross platform library inside the Flutter app, doing the demanding work right on the device. Think fast calculations or encryption.
The front end case is the one we love most. Write the heavy part once, run it on every device the app supports. The user never waits, and private data never has to leave the phone just to be processed.
How we actually decide
There is no grand theory here. The rule fits in three lines:
- No backend needed? It is just Flutter.
- Need a backend? Flutter for the app, Go behind it.
- Something has to be seriously fast? Then, and only then, Rust.
That is also how we work on client projects in our custom development work: start with the simplest thing that fits, and add a piece only when the product genuinely asks for it. Power you do not need yet is just maintenance you signed up for early.
So that is the whole secret, and it is not much of a secret. Fast to build, easy to test, cheap to keep alive long after launch. None of it is flashy, and that is on purpose. The point was never the languages. The point is software that does not turn into a burden the day it becomes yours.