17. 10. 2023
8 min read
Code with purpose: @Import product thinking to your code
We bet there are lots of engineers among you with their own projects that’ve been brewing for over a year in the bottom drawer. The purpose of this piece? To ensure you're not like that punchline engineer who spent 500 hours on a CMS only for it to gather dust. Let's make your work matter and save your precious time!
Once upon a time, a developer said: 'I'm just building a simple blog.' 500 hours later, a custom CMS emerged for just two posts! Sounds familiar, right?
And of course, as a proper CMS system, it should also have its own Kubernetes cluster!
When I began my coding journey 10 years ago, I was quite green. My first project took months, and it was like a ghost - no one ever saw it. My next endeavor was a nine-month secret affair, unseen by all. Sounds like a magic trick gone wrong, right? I was guilty of overengineering and lacked a product mindset. But, things have definitely changed since then.
At the last Tech Talk, it was said that you can't teach product thinking to an engineer, but I'll show you otherwise. Over the past decade, I've churned out platforms such as Optiomat.com, which makes ESOP plans for companies, as easy as pie, and my latest pet project, VueFileManager.com as a file-sharing platform.
Make your work matter and save precious time
I bet there are lots of engineers among you with their own projects that’ve been brewing for over a year in the bottom drawer. I know a few and I bet you do too. The purpose of this piece? To ensure you're not like that punchline engineer who spent 500 hours on a CMS only for it to gather dust. Let's make your work matter and save your precious time!
You can face only 2 end results, it’s up to you which one you choose
Despite all your hard work and resources, no one's using the application you crafted so meticulously. Still, take pride in knowing it's the best-coded application you've ever deployed.
You created a usable, rather than a great, app, and suddenly it experiences an unexpected surge in popularity and usage, leaving you unsure of how to handle the flood of users at first with your broken code.
So, you get to pick between these two issues. And the typical engineer's mindset often goes for the first one. Let's create a super rad app, let's get lost in the tech details, and let's follow all the best practices like DRY, SOLID, and various design patterns. And boy, does that consume time!
Some engineers love to make their work tricky, always cooking up ways to make it harder. Take Tailwind, for example, it's a simple tool to quickly create/update your application's frontend look - its main perk. But then, some engineers came up with something like Typesafe Tailwind - adding a whole new level of complication!
Anyway, want quick results and efficient use of your time? Enjoy your work by momentarily forgetting EVERYTHING you've learned about programming. Say bye-bye to SOLID, DRY, Design Patterns, and the rest for a bit.
How not to do that
Simple or high-tech development - you can have both of the worlds
I'm going to walk you through a project I designed and coded - a subscription module for my VueFileManager platform. Think of this platform as your iCloud or Google Drive, you can store and share files there. At that time, the subscription module didn’t exist. I sold it as a platform. So you buy it, you use it on your server. Some of my customers, as creative as they are, thought about setting up a SaaS business with it. They wanted to sell subscription packages and make some dough out of it.
So, my main aim was to see if creating this subscription module would boost my income. That was the only business metric I cared about. The goal was simple:
The admin had to hop into the app and whip up a subscription plan for the user. Let's say, for 10 bucks you make a package with 200 GB storage and then watch who's buying your subscription.
Users choose a plan, register their credit card, and sign up for the plan. Then, they can cancel whenever they want. And that's about it.
These were the basic functions, without adding any unnecessary fluff.
I aimed to spend no more than 2 weeks in design, front-end, and back-end. So, what was the technical scene like?
The code lacked abstraction and the Subscription module was tightly knit with the entire platform and codebase. Edge cases weren't supported, for example, if a user's credit expired, no issue would arise as we hope in the first month users’ cards won’t expire. Only one payment provider, Stripe, was added. As for the Stripe connection, all calls were made from the API service to the backend, processed, and then forwarded to the frontend. That meant users could wait up to 3-5 seconds for data loading. Finally, everything was done in 2 weeks.
Get user feedback
It's feedback time, a chance to reflect on what we've created! Here, we learn about feature requests and usage patterns. Feedback shows us which features need a glow-up or new additions, and uncovers those sneaky edge cases users face. We can pinpoint where our app needs more abstraction for sustainable development and robust functionality. With the feedback, we can plan necessary refactoring and spicy improvements.
This was a game-changer as we nailed our business goal. The revenue boost 3 times (as the commercial license is more expensive), allowed me to concentrate on further evolution, like tweaking the code and the subscription system, driving our platform towards prosperity. Whoop-whoop!
Post-MVP plan - now all hardcore engineering skills come into play
Switching gears to serious development mode now - the most crucial part of the entire process, where all hardcore engineering skills come into play. So, I had to draft a post-MVP plan; outlining everything I needed to implement and improve.
The key move was setting the subscription module free from the main codebase. Now it's so versatile,
you can pluck it and use it on any platform that has the same ecosystem. But I didn't stop there! Based on the feedback from users, I added other payment providers besides Stripe. For instance, Paystack and of course, PayPal. Many business owners who bought the platform wanted to jiggle in their own payment gateways. But that was nearly impossible in the initial MVP. So, I made 'easy extensibility for other engineers' my goal!
This is how I'd break down the structure of a decoupled system (see the pic on the right). It's a database, the heart is the core base, and a chatty subscription module interacting with payment providers like PayPal, Paystack, and other gateways. The modules then just played a game of data exchange based on their needs.
To keep our development sustainable, we needed to whip up some abstractions for managing plans, subscriptions, and credit cards. Here's a simplified example. Picture this interface chock-full of methods like "create a plan", "update plan", and "delete plan". I brought in the Adapter pattern.
Showcase of creating a plan to allow user subscription (Hardcore engineering version)
Let's say we want to create a $10 subscription for a 200GB per month plan. Now, because gateways always need their unique implementations and I want to create a plan for all supported payment providers, I had to develop an interface for this. Adopting the adapter pattern in my app was beneficial.
The admin creates a subscription plan on the platform, adds a catchy title, and adjusts the remaining features. Afterward, they send it all into the platform's backend. Then, they can relax with a cup of coffee while the rest chugs along asynchronously. The subscription module takes charge to ensure everyone from Stripe, PayPal, Paystack, and other payment gateways sees the plan. It is then neatly stored in the database. The task is to ensure that the plan is included in all payment gateways so users can select their preferred payment method and subscribe later on.
And if something goes wrong, a rescue mechanism will retry until it works. Worst-case scenario, an engineer is called up to fix it when the system fails. Once patched up, our database syncs and the plan goes live. Users and the app have quick access to plan data stored locally as it swiftly communicates with the payment gateways. When data is updated, whether on the payment provider's side or the platform's, the module updates everything.
So, I really got to flex my engineering muscles in the simpler parts of the app where you can let loose and apply all you've learned. Some things were clear, like functions needed for a classic subscription system, others were cool discoveries, like metered billing. If you want to limit something for each function, you need a Pay-as-you-go model, usage tracking, a credit system, billing alerts, scammer protection, and so on. Syncing and caching were sorted, so users had lightning-fast data access. Then, you could tackle other quirks you identified. Plenty of room for a coder to really flex their skills here!
So, take your pick. Either you spend 6-12 months creating an app with uncertain results, or you take the road of swift experimentation and a no-hassle approach. Next, you hit a checkpoint, release your creation into the wild, and snag some valuable user data. This lets you as developers see how users interact with your brainchild and how to further polish your code.
Armed with that information, engineers, product managers, and stakeholders can make informed decisions, boosting the odds of success.
This is the way.
If you've opted for the rapid, minimal-effort approach to product building, stay tuned for my upcoming article where I'll share practical tips and everyday programming insights, complete with code examples.