If you haven’t heard the term before it’s used to describe a user or system flow that goes through all the expected steps without any issues. Everything else is referred to as an edge case, or a state that the user or the system entered that was unexpected or unplanned, which may even trigger an error report.
I am going to use the term differently to describe the day-to-day life of any engineer leading a technical project 😉. Projects come with various levels of difficulty and duration. You may work alone or with others, you may have 3rd parties you are integrating with or you may have a timeline you are working towards. In all cases tho it’s a huge win if you manage to stay on the “happy path” for your project.
So what is the happy path for a technical project? The happy path of a project for me means being able to deliver the functionality you set out to implement, in a good way and roughly on the expected time scale.
So what is a “good” way?
The definition of a good way may vary by person, sector or even project. For me it means a system that’s tested and monitored. It should provide you with enough confidence that is working for all common cases and that it will notify/alert if there are cases that it cannot cope with (high load, inconsistencies etc).
And what is the expected timescale?
The expected time scale is a very misunderstood area of system’s design and implementation. For me it is not about the estimates or the deadlines that often get imposed. It is about the relevant measurement of time.
For example say you and your team estimated a small project would take between 2 to 3 days and it actually took 15 days to complete. This would mean that something very important was missed somewhere since the project took 5x the anticipated time. Or put another way something that we thought it would take days actually took 3 weeks.
Similarly something that was expected to take 2-3 weeks taking an entire quarter to complete would again mean that the week scale got turned to a monthly scale. If this happens consistently for all projects it could really hurt your team’s moral as you would always be running behind instead of leading with confidence.
So what is so hard about that?
Just about everything 😉 I would say. When building something new, no matter how well you have prepared, no matter how well documented your requirements are, no matter how well that API is defined you will always encounter some unexpected behaviors (and yes this is universally true).
These behaviors will require your and your team’s attention. That extra level of complexity may require more thinking, more time for implementing and in extreme cases a redesign of parts of your newly created system or changes to other parts of existing systems in order for things to work in unison.
And this is where you can help your team a lot by keeping an eye out for these things and using your past experiences to anticipate problems and have a fall-back or alternative solution ready for when this happens.
A few tips – just in case 💡
- Split your system design into components with clear interfaces
The benefits here are many as this approach creates a clear logical separation that leads to better overall design, creates building blocks that encapsulate functionality cleanly, allows for changes to be isolated within the components etc
- Define how the components interact from the beginning
This will allow you and your team to work on the contract of how the components should interact and unblocks you to be able to work in parallel
- Re-use components
Keep an eye for similarities with things you have build in the past. You will be surprised with how often things come up that share a lot of the same characteristics. Can you re-use a component you already have by extending it or solidifying its interface? It’ll save you a bunch of time and you can put your efforts into improving something that has already been tested instead of starting from scratch.
- Simplify (based on scale)
Build good, solid functionality for the needs you have today that will work if you grow at 100x or 1000x or even 10000x from where you are but don’t build for 100000000000x as by the time your system grows that much you may have refactored it 10 times over
- Simplify (based on functionality)
On a similar note one of the best things you can do is really understand which parts of your system and requirements are the ones that stand on the critical path of your implementation. Focusing on completing these before moving to parts that you could easily do without will allow you to spend any extra time on things like testing and early integration which is a great way to increase confidence on your implementation. And yes, this is usually easier said than done 😊
- Fall-back solutions
In many cases there are more than one ways to solve the same problem. Can you think of different ways of implementing the same thing? It might be that there is a different solution that you can introduce if need be or one that can act as the stepping stone towards the longer term solution that you have in mind.
And finally a lesson I learned which has been extremely useful: instead of focusing on individual components one at-a-time, start looking at systems at a macro-level as well. You will get a completely different perspective of what’s possible!