Nobody Gets Fired For Choosing Java
Choosing the right programming language for your payment application is about business requirements, not your engineers' taste
Welcome to Money In Transit, the newsletter for startup founders who find themselves dragged into payments technology. I’m Alvaro Duran.
Today’s post explores what makes a good programming language for building payment applications. It’s tempting to say that all programming languages are equally valid, but the reality is that the companies that haphazardly choose what’s familiar are at a disadvantage.
And disadvantages, in the startup world, lead to failure.
Consider sharing this post with a non-technical founder who is considering outsourcing the development of their payment application.
Any tech founder worth their salt is deliberate when choosing their startup's technology stack.
I don’t mean choosing Confluence over Notion. Those decisions are inconsequential. The success of a tech startup always boils down to picking out the right infrastructure. Only when that happens, the startup has a chance to build remarkable products.
If relational databases aren’t a good fit for payments, the logic that enforces their rules has to live elsewhere. And the only place left is the server. By making your data storage simpler, you’re moving the complexity elsewhere.
Non relational databases demand stronger programming languages.
But what is a strong programming language?
If programming languages were equal, then there is only one possible choice: the standard. And standard, in finance, is Java.
You’ll find Java running the most important workloads at many banks. That’s because banks are very conservative when it comes to technology. And count on Java because, at some point, it was the “professional” programming language. Nobody gets fired for choosing Java.
But Java wasn’t there when programming started. It established its supremacy over the alternatives. Should a better option appear, Java won’t be the standard any longer.
That’s where tech startups come in—they’re experiments on software tooling.
You don’t believe me yet? Let’s do a pop quiz!
I'm about to show you two quotes from two technical leaders, employed by two different companies. I’ve chosen these companies because:
Both are fintech companies subject to complex regulation
The most critical component of their business value is technological
The tech stack chosen was out of the ordinary
But crucially, because one of them is making billions, and the other one laid off 20% of their staff two weeks ago.
And I want you to guess out who’s who.
The tricky part, though, is that neither of these leaders is talking about business, or strategy, or anything like that. No. They’re talking about which programming language they chose for their companies.
OK, so here’s quote 1:
Our previous company was Node JS, and we started with [version] 0.6, so you can probably see that we aren't afraid of picking technology based on their benefits just because they’re “new”. [...] We also tend to love functional [programming languages]. We found that by using functional languages or functional approaches—you can do that in Javascript as well—you tend to avoid a lot of mistakes [...]. Elixir was a functional language, with a battle-tested runtime. One of the things that I personally really like about it is how macros are implemented [...]. It’s a pretty succinct Ruby-inspired syntax, different from Erlang. I'm not a big Erlang's fan, because of the syntax.
Hold your thoughts! Here’s quote 2:
One obvious question about this is “who the hell cares?”. Why does the distinction between [imperative and functional programming] matter? I think the reason it matters is because mutation is just a way of getting a computation done [...]. In general, when you have mutable state in your program, that mutable state is more than just a way of computing things. It’s a communication channel. It is a way for different parts of your program to talk to each other. And the reason you want to avoid that, when you can, is because having a program that is cleanly separable into pieces that don’t talk to each other is really valuable for having a program that’s easier to reason about as you compose it together.
Want to know which one is which? Read below.
Quote 1 is from Jonathan Lima, founder engineer at Brex, a credit card processor that two weeks ago cut 20% of their workforce. Quote 2 is from Yaron Minsky, who leads the technology group at Jane Street, a Wall Street firm that made $7 billion in the first half of 2023.
It’s hard to predict if a programming language is going to be a good fit for your company. It impacts hiring, it constraints program writing and guides engineers. Like in normal languages, it makes some things easier to say, and others harder. Programming languages not only describe reality, they also shape it.
The programming language you choose is much more than syntax. It contains an ontology. And in the end, it drives, to a high degree, the success of your company.
Programming languages matter.
Finished is the Same as Abandoned
What makes a company a tech company? Its recursive property. A tech company, unlike the rest, focuses on “how” it builds as much as on “what” it’s building. Tech startups use technology to help their users. Non-tech use technology to help their employees.
It follows that its competitive advantage depends on the technology it uses.
It also explains one of the most intriguing aspects of tech companies: how often they give away the technology they produce. Open source software highlights how uncritical a finished piece of technology is. Tech companies force all tech decisions into the open, and in doing so put their non-tech competitors out of business.
The ability to understand and improve your system is what’s valuable. Software that’s “finished” is as valuable as if it was abandoned.
Engineers designing payment applications emphasize availability over performance, reversibility over correctness, and maintainability over scale.
Paying fast isn’t as important as Being able to pay.
Failures aren’t as important as Bad data.
Serving a lot of people isn’t as important as Serving people a lot of times.
These engineers build on 3 pillars: consistent jargon, an emphasis on testing over type systems, and debuggability.
Consistent Jargon
There are few things that are less logical than the regulation around moving money.
When engineers build things like operating systems or databases, they aspire to keep the whole thing logical. But when they’re given things as ambiguous as PSD2, their gears grind.
Of course, they got that way for a reason. Payment regulation has evolved as a whack-a-mole process. One built on top of the fragile balance between making things hard for fraudsters and making things easier for society. Engineers building money software deal with a thousand one-off special cases. It’s not a justification, but a fact of life.
What engineers can bring to the table is the ability to build machines that enforce a consistent terminology. They use programming languages as a way to systematize this “logic”. When engineers succeed, collaboration is natural.
Domain experts provide good answers. But engineers provide good questions.
Strong Testing over Strong Typing
Traditional programming languages build on top of type systems. These are a mechanism that address the same mistake that relational databases did. The inconsistent manipulation of data.
If we already ditched the relational databases, why would we keep types in our programming languages? Because engineers can change types as fast as the rest of the code. However, just because the machine doesn’t complain about types, that doesn’t mean that your logic is correct. It’s an incomplete sanity check. To ensure that your application does the right thing, there’s a more powerful tool: testing.
You could use some static types to make your program faster. But as far as correctness goes, testing wins by a landslide.
Debuggability
Here’s a secret that engineers don’t often say out loud: there’s no way around testing in production.
To achieve high quality applications, engineers write code that’s good enough, ship it, and find and fix all the bugs there are one by one. That’s because finding bugs is straightforward, and shipping perfect software is impossible. Good testers vastly outnumber one-of-a-kind engineers. High quality software is debuggable.
To make debuggable software, engineers ship it alongside the tools that would make finding a bug’s root cause easy and the first time it happens. Debuggable payment applications leave breadcrumbs of what they do, and how they made decisions.
Debuggable software answers questions about itself.
Payment Applications are Engineering Problems
The consequences of hubris will always be hiding in the parts we don’t yet understand.
Payment applications built with a focus on iteration always win over those that “just work”. That’s because what we need from them is clear—but what is needed changes all the time. To keep pace in a shifting industry, with absolute control and zero downtime, is the hallmark of successful money software.
Change. Adaptation. Growth. It flies in the face of those who think that external providers can do the job for you. Nailing down payment applications is the best bang for your buck.
It all starts with a choice: to pick out the right hammer. So that you can start pounding.