MVP Tech Debt Winter 2022 Edition
While most MVPs fail, a few manage to succeed despite technical trade-offs that minimize time to market. Promises made to pay off the tech debt are rarely kept.
It’s easy to say that MVPs should be discarded as soon as Series A hits the bank. There are few public data sources (here’s one) available to know for sure what happens to most MVPs, proofs of concept, and first swings. Based on over 30 years of software development experience, I would argue that start-ups rarely throw away their first attempts because investors introduce even more pressure to grow the business and build more features. There is no time to stop to unravel unfortunate decisions. A rewrite only happens after the chicken wire and duct tape fail to hold.
These are some of the significant decisions made by myself and colleagues that are currently haunting me. Of course, there are many more, but I don’t time to list them all.
Whether the problematic nature of these approaches manifest themselves immediately or years after their deployment, they are near impossible to fix without grit, cash, and good graces with management.
1. Synchronous and nonidempotent mutations
The longer an operation takes the more likely a client is to receive an error. Can they safely retry?
If you’re designing an API and you’re unfamiliar with request ids and asynchronous APIs, educate yourself immediately. Mutating remote data without both will cause you endless trouble at the worst possible moment.
2. Failing to plan for auditors
In case you need to eventually build an audit trail of data modifications, store API request ids (see the previous item) with your data (e.g., a column in every table). Persistent storage is likely to become the data source for audit trail observers. Request ids are a quick and dirty way to join disparate systems. If you plan ahead, you will get this capability with almost no effort. This isn’t a perfect solution but something is always better than nothing.
3. Betting the farm on NoSQL data storage
NoSQL is an excellent choice for a demo involving a maximum of two concurrent users and simplistic requirements. You will however rue the day you didn’t use a database with unique constraints and consistency guarantees before understanding the needs of real users.
Ride PostgreSQL to the end of the road! It will take you further than you think. While NoSQL addresses high-volume low-latency use cases in the best way, NoSQL also makes easy things hard.
4. Remote APIs that return data instead of resources (aka graphs)
We’ve all been there. The frontend view is “just” a table. An endpoint that returns an array of key-value pairs satisfies the use case.
Your manager’s manager is telling everyone to keep it simple, stupid! and they’re asking “Why would anyone over-complicate such a simple task?”
Is the easy way the best way? Absolutely not!
Client application-based API design is one of the best ways to paint yourself into a corner.
View-oriented endpoints are not reusable. Their result sets don’t match the intent of RESTful resources — they are instead a Frankenstein’s monster of inseparable amalgamated resources. When the frontend view changes, so will its corresponding backend endpoint.
Design resources. Return arrays of them. Make sure the frontend team uses a library that converts nested objects to arrays of components without hand-coding this basic universal need.
If performance trumps all other concerns, save yourself the headache and intentionally use RPC instead of REST.