Building a custom application is an investment, and a process. Software is never done. It’s ever-changing. It lives in an ecosystem of constantly updating devices and browsers. It serves customers who are fickle. It hooks into other pieces of software, which are themselves ever-changing. This needs to be at the forefront of your mind when you start any application project.
If you’ve ever worked with an application that’s maybe a few years out of date - if you’ve ever requested a small change and been told it’ll take an unreasonable amount of time - you know this pain, and it’s no easy task to avoid.
Since we can’t always plan for a specific change, we need to plan for change itself. Thankfully, the software development community has been doing this for a while, and there are a number of strategies we can employ.
An application has all sorts of parts - a user system, maybe a payment gateway, email workflows – and each of these parts needs to talk to one another, but what they should not do is depend on each other.
A modular approach to architecture seeks to isolate each piece of functionality into its own ‘module’ and then provide a common interface for these modules to interact. I won’t go into discrete technical detail, but here’s a practical example of one approach.
Let’s say that when we create a new user, that user gets a welcome email - simple stuff. We’ve got two parts - a user creation process, and an emailer.
A non modular approach might be to have the user creation process run, then tell the welcome email to send. Seems logical enough, but what happens when we need to change how we send email? Or we need to change which email is sent? Unfortunately, we’ve coupled our user creation process with our email, and any change to the email, affects the code inside the user creation process.That sounds messy. The user creation process should only care about creating users, not about anything else.
Instead, a modular approach might be to use an events system. When the user creation process finishes, it fires an event - something like ‘user.created’. Then we have our emailer listen for the event ‘user.created’ and decide which email it should fire off. We’ve just created a simple interface for these two parts to interact - our event system. If we need to change something about our emails, the user creation process remains unaffected. It happily still creates a user, then fires the event. Any change only needs to happen within our emailer.
And what’s better, if we decide that something else needs to happen when a user is created (maybe we get a credit report, or validate the user against some other database) - no problem at all. That new process can also listen to the ‘user.created’ event and continue on without changing the user creation process at all.
Now that’s a simple example, but think of it in the context of unforeseen changes. We’ve architected our application in such a way that each feature can worry only about itself, and interact through the common interface of firing or listening to events.
More than just implementing things, building an application requires planning and identifying the features you actually want to implement.
A non-scalable approach might be to list out all the things you want your application to do, then get building - finishing when all those features are complete. But that’s not looking at things modularly, that’s looking at the application as a single entity.
Instead, we could organize our efforts by feature. We could still list them out, but this time organize them by priority and tackle one feature at a time. This subtle change gives us the flexibility to change the priority of a feature, or maybe slot a new one in, or remove one that we realize is no longer important. Now we’re looking at each feature as a single entity, rather than at the whole application.
We don’t need to build everything before we release our app, and we don’t need to wait for massive development cycles to make an improvement or a change either. Each time we’re ready to begin a new development cycle, we ask “What’s the most important feature for the business right now”. This gives us shorter development cycles, and more frequent feature releases. This empowers the application owner to adapt to changes.
When we make a change, or add a feature, we’ve got to not only make sure that new feature works, but we’ve got to make sure nothing else has broken, and then we’ve got to get our change up onto the production server. Have you ever seen an application add a new feature, only to break some seemingly unrelated feature? This is what we’re guarding against.
Our modular architecture should help us prevent any incidental changes to other parts of our application, but automated testing helps us be certain, and helps us identify any incidental changes that might have occurred.
We write automated tests for each piece of functionality. These run through all the parts of that feature, and ensure they work correctly - give us the output we expect. Then we set these tests to run every time code is committed to the application, before anything is deployed. If any of these tests fail, the deploy cancels and alerts us of the test that failed. Now, not only did we prevent a bug from deploying, we also know exactly where something is failing and have sped up our debugging considerably.
Of course these aren’t the only strategies for ensuring our application can scale and change, and each of these strategies takes a bit of time to properly consider and implement. Ultimately though, if you’re building an application that you hope to run for years, to grow with your business and continue to provide value, a little bit of extra time upfront to architect things correctly – to put in place the right processes – will save both time and money over the life of the application. We aren’t building cheap, throwaway products. We’re building clean, maintainable, changeable applications that can power your business for years.
Do you need a team to help you create sustainable business solutions? Contact us today.
Want to make a good first impression? Pay attention to your homepage (your users already are!). Here are some tips for great homepage design.
How do you make a minimum viable product that gets all parties' buy-in? You prototype. See why prototypes are a required step in app design.