Building modern apps is exciting. You get to learn new technology, converse with people in industry that follow the same patterns, and explore new designs every day.
Imagine you are given the opportunity to build greenfield application and you opt for tech decisions that have good industry backing and support, like serverless. You build your app over the course of months or years and when you go live, you're done! Right?
Not even a little bit.
First of all, software is never done. You have invested in a time and energy black hole. There are new features and enhancements to build, defects to fix, and optimizations to be had.
You also have to take a step back, look at what you've done, and ask yourself "is this still relevant?"
If you took 2 years to build your production app (which is a totally acceptable time frame), chances are there have been a significant number of enhancements and new managed services released by your cloud vendor. Did you freeze your app in the moment in time when you started development?
Honestly, it's easy to do. But what should you do when you find yourself in that situation? You could say you have a modern-ish app and call it a day. Or you could roll up your sleeves and bring the tech up to the latest and greatest.
The answer to this is short and sweet: total cost of ownership.
You want to keep your app as up to date as possible in an effort to make the total cost of ownership low. You want to minimize how much it costs the company to maintain your application. The lower the maintenance costs, the higher the profit margin. The higher the profit margin, the greater likelihood that you get to do it all over again!
If you have questions on how to troubleshoot an issue with modern services, there's a giant community of developers out there with answers. If you hire someone new, you have a large pool of individuals you can pull from with the skills necessary to build your app. Your developers will be happier and more productive continuously enhancing their skill set.
Newer features oftentimes means faster performance. Incorporating enhancements that affect performance will boost the confidence of your customers in your product. Higher confidence means greater retention. It also means you have a higher likelihood of getting word of mouth references. Sounds like a win-win!
There are numerous benefits to maintaining a modern application with the latest and greatest tech.
A common misconception around refactoring is "everything else must slow down or stop in order to do it." I'm here to happily say that is not true anymore. Refactoring as you go is one of the many benefits you gain when you decide to go serverless.
So let's focus on how we can incorporate new cloud services while still make progress with new features in our app.
Cloud moves fast. Every time you blink, your cloud vendor has released some new managed service or feature.
While not everyone has the luxury of consuming new features as soon as they come out, they need to be on your radar. What you don't want is for a new feature to come out, then another one that builds on top of the first, then another that builds on top of the second. Before you know it, you're 3, 4, or 5 iterations behind and the gap to catch up is no longer trivial.
Let's take AWS Lambda as an example. Say you've built your application on the nodejs10.x runtime for all of your Lambdas. At the time, it was the latest and greatest. You continue developing your application, ignoring new Lambda features and supported runtimes.
Before you know it, the industry is all on nodejs14.x and end of life for 10.x is knocking on your door. You have to update build images, CloudFormation scripts, and potentially change all your Lambdas all while under the gun because support for your runtime is dwindling.
Focus on baby steps, not leaps and bounds.
If you had jumped to 12.x when it came out and were aware of the release of 14.x, the difficulty of modernizing would be significantly less.
When you commit to adding a new service or feature into the repertoire of your application, focus on starting small. The beauty of the cloud, specifically serverless applications, is isolation.
Every endpoint is isolated in a serverless API. Each component of an orchestrated or choreographed saga is configured, built, and controlled independent of its peers. You have the ability to start small, verify results, and expand.
Almost think of this approach as canary development.
Canary deployments are a strategy used to release new features to a subset of your customers. You identify a group/size, release software to them, verify backward compatibility and app health, then expand your group size. Rinse and repeat until the new version is live with your entire customer base.
When refactoring, you can take the same approach. Identify an endpoint or functional area in your application to incorporate something new. When development is done, verify the results and incorporate it into another area of your app.
The phased approach is great because it doesn't stop development down in order to do a refactor. You can enhance your application as you make routine maintenance changes.
An added bonus of canary development is the opportunity to improve your understanding every time you make a change. The first time you make a change you likely have a cursory understanding. The second time you use it, you are familiar and things are a little more intuitive. The third, fourth, or fifth time you approach it, you understand the nuance and build with intentionality.
If you had stopped down and refactored everything at once, you likely would have implemented the feature with a cursory knowledge and either missed out on an important aspect or maybe left some cost optimization on the table.
If there's one thing I know about developers, it's that they tend to go overboard. Changes come in tidal waves. Massive blocks of significant change where there is no control.
That's a developer's nature. Make big changes, organize, get it done.
I'm guilty of it, you're probably guilty of it, it's a whole thing.
We need to stop that.
If you're working with serverless, chances are you are incorporating CI/CD into your deployment pipeline. A major premise behind CI/CD is small, incremental changes. When pushing to production 10+ times a day, you need a way to monitor change control.
If you're refactoring your entire application at once, that paradigm goes straight out the window. You get an unmanageable amount of change that is impossible to rely on automated tests for.
Do your best to divide the work into small pieces. You want to identify any downstream effects before moving on to the next piece. If you changed the entire app at once, it would be impossible to pinpoint the change that caused the incorrect behavior.
By dividing refactor work into small, digestible chunks just like you would for new development, you get isolated control, a smaller blast radius, and the benefit of learning from your mistakes the next time.
When building serverless applications, you must be comfortable being in a constant state of refactor. There will always be new services and features that will enhance your app. You have to be responsible and identify the changes you should be integrating.
Don't wait too long because you don't want to fall behind and make it next to impossible to re-modernize.
Cloud development is an anomaly. It's one of those things where vendors keep making the services better and faster while also making them cheaper. Continuously refactoring your application will allow you take advantage of this anomaly.
When it comes to total cost of ownership, newer is better. Better support from the community. Better features for your customers. Better developer experience. Better cost to run.
We don't modernize because we "think it's cool" to be modern. It has serious impacts to both the products we produce and the bottom line of our companies.
If you aren't already, start sprinkling in refactor stories in your next sprint. You don't need a dedicated effort in order to make a big change. Your application will evolve over time, just like the tech behind it.