Let's continue with the "Stages Of Your Software Development Career" series!
I find the senior developer career stage one of the most interesting because there are so many different skill sets and targeted focuses that senior developers can have.
Recap Of The Last Article
Last time, we looked at topics like:
- Who's a senior developer?
- What's an indicator of being a senior developer?
- Some traits of senior developers
- How experience plays a role
- What scope of skills senior developers should focus on
- Other various tips
Intro
I want to dive into an important point I made last time (worded a bit differently):
An essential skill you must have is the ability to view potential problems as a series of patterns that have already been solved somewhere else.
This is essential if you want to become a senior developer.
Not a senior developer in title alone, but in skills and competence!
A big part of growing in your career is learning about more tools, techniques and patterns that enable you to solve more difficult problems and solve them more efficiently.
This will help you build trust, reputation, respect, etc. which will eventually lead to more career progress and options.
Design Patterns In Real Life?
Let's look at the classic software design patterns.
Design patterns are (usually) not meant to be something you use up-front when creating software. They are designed specifically as solutions to specific problems you might face in your software's architecture/structure, behaviour, etc.
Adapter
The adapter pattern, for example, is used when you have two or more different interfaces which need to communicate or be integrated with each other.
In real life, this works much like an adapter for a socket wrench.
Well....it's actually the exact same thing!
This is actually a very important point: the adapter pattern doesn't only apply to code/software!
In fact, all these patterns aren't just for software design!
We see it being used in a physical sense with the socket wrench, with USB adapters that allow our mobile phones to communicate with our computers, etc.
Note: It's also similar to the Anti-Corruption layer concept found in Domain Driven Design.
Facade
Let's take another.
The facade pattern is used when you want to simplify a complex system/interface.
For example, the process for submitting an order in some web app might involve a complex series of steps to occur.
We can "wrap" the process into a one method call that will give the client a really simple way to execute that process.
Just like with Amazon or eBay, they might have a "one-click buy" feature which will automatically run you through the steps required to purchase.
This is a facade.
This pattern can also be applied to more "real world" contexts like automobiles. Using a manual transmission involves knowing how to use the clutch, how to use one of your hands to shift the transmission stick at the same time, keeping your eyes on the road while doing it, etc.
However, most cars today do all of this automatically for you (I suppose that's why they are called automatic transmissions? ๐).
This is the same kind of pattern but applied in a non-software oriented context.
The underlying pattern is about simplifying how a client interacts with your product - regardless of what domain it's applied to (software, automobiles, etc.)
Systems Thinking
This idea of using common patterns to solve problems in different domains and contexts is sometimes called "systems thinking".
Scott Hanselman has also recently blogged a bit about this idea.
By understanding the fundamentals around building systems and the patterns that can be used to solve problems, you can become someone who can bring value to businesses no matter where you are placed!
P.S. This article is originally from YourDevCareer.com where you can check out more articles and resources to help accelerate your career growth!
A Real Example
A conversation I was in recently involved some performance issues with a legacy application running in .NET.
One of the issues we discussed was around degraded system performance under a high load.
The overall problem is that the system doesn't have enough "workers" to handle many HTTP requests all-at-once.
The immediate solution offered by a co-worker was "we need another server so we can use load balancing."
This solution adds a new node/server and places a middleman between them and the client. Kinda like the mediator pattern?
However, my response was that the best (and cheapest) fix is to address the fact that all database I/O in that system is synchronous.
If we make all those I/O operations asynchronous, then those threads will become available to handle any new HTTP requests while the database is working in the background.
This solution, at a low-level, uses the futures/promises pattern.
But at a higher level, it's the same solution that you use when baking in a recipe!
Efficient Cooks Don't Need A Master Chef ๐ค
The solution of adding a middleman and more nodes, instead of making the workers you have more efficient, would be like adding a master chef to your kitchen and also hiring another person who you can delegate tasks to.
While one worker is staring at the oven while it bakes, the chef will order the other worker to make the frosting.
Sound like some companies you've been in? ๐
However, if the one cook you have is already working at a high-efficiency then you might actually need to introduce another worker and possibly a master chef to delegate tasks.
But, if that one worker isn't working efficiently, then you probably want to make him/her as efficient as possible first, before hiring more employees (just like making your threads more efficient using async I/O).
Hopefully, this example and the kitchen type of analogy can help you see how there are these overarching patterns and techniques that can be applied in technical and non-technical contexts.
With Knowledge Comes Great Power
Part of the issue though, in this case, is understanding the root problem.
Sometimes, even when we know the real problem we may not be aware of the fact that there exists a specific pattern to solve it.
My co-worker, for example, is aware that the database I/O is synchronous. But perhaps was unaware of the fact that there is a more efficient method.
In the absence of a solution, the thought process has to move up to a higher level and apply a less-efficient solution.
It's essential to learn about design and system patterns at least to the point where you know what problems have solutions!
You don't need to know how to implement design patterns, but you better know what problems they solve.
Once you come across that problem, you'll remember, "Oh ya, there's a pattern for that I remember reading about..." ๐
Some More Deep Dives
Let's take a look at a couple more examples and see what familiar patterns apply.
Example: Requesting A Restaurant Meal
Queues:
When taking your order, the waiter might place your order into a queue since the cooks are busy with other orders right now.
Chain of responsibility / Pipes:
There may be a series of cooks who need to look at the order, complete their part, and then pass it on to the next cook who can spice it up, garnish it, etc.
Master-slave:
The chef might assemble a complete meal from the individual dishes the lower-cooks made.
Example: Banking Transactions
Ledger / Append-only / Event Sourcing:
When banks deal with account transactions they never erase a previous event or transaction.
For example, if PayPal mistakenly pays you $100, they won't "erase" that transaction. They will issue a corresponding counter-transaction (e.g. take $100 back from you). Even though both parties have the same amount of money they started with, the transactions themselves remain.
This is quite different than the typical CRUD type applications most companies build. These usually just store the current state of the system.
But this type of append-only pattern addresses problems in a system that needs:
- Ability to audit all actions performed on the system
- Ability to responsibly manage shared resources (like money) between multiple external parties
- Ability to perform analytics across trends in the system (since you have recorded every event in the system)
- Ability to query the system at specific instances of time
Sagas / Distributed Transactions:
When you do something like an interact e-transfer (e.g. I want to send money to my mother who is with a different bank altogether), there involves an inherent overall "transaction" (like a database transaction).
Except, this spans multiple systems (your bank, another external person's bank, other third-parties, etc.)
If you send $100 to your mother, then:
- It is "taken out" of your account (at least, you don't see it).
- Your mother has 30 days (or whatever) to accept the money.
- If she does, great!
- If not, then your bank has to perform some kind of compensating action (e.g. Put the money back into your account.)
If any step fails, you want to make sure the money is put back to the correct place. You don't want to lose data! That would mean lost money ๐ฏ.
There is a complex series of steps involved that aren't tied to a specific database or even a specific system, yet needs to be part of a consistent database-like transaction to ensure your money either gets to your mother or eventually comes back to your account.
Known as distributed transactions (i.e. transactions that span multiple systems), there are specific patterns that help deal with these kinds of scenarios - like the saga pattern.
That's quite a bit advanced but worth looking into if you're interested.
More Resources
I hope you've learned something new that will help you think about solving business and technical problems in new ways!
The main point to remember is that you don't need to know how to implement these patterns, but simply know that they exist and what kinds of problems they solve. You can become a superhero if you can intervene when, for example, designing a system and saying, "Hey, I remember there was a pattern that solves this already! Let me find it..."
If you want to dig into some of these more "senior" level topics and areas around systems patterns, here are some resources I've found the most helpful in my own career.
Free:
- An awesome free collection of system patterns that are written for architecting Azure applications - These are general enough to be very helpful.
- Jimmy Bogard (blog and YouTube)
- Udi Dahan (blog and YouTube)
- Martin Fowler
Books:
- Patterns of Enterprise Application Architecture - Full of small practical techniques and patterns that can help when dealing with large or complex codebases and systems.
- Building Microservices: Designing Fine-Grained Systems - Probably the best primer on distributed systems I've read. It's not super in-depth into any particular area, but deep enough that you'll learn tons about how to design and think about more complex systems and problems.
- Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions - More of an advanced look at specific patterns around distributed systems. Focuses on patterns you can use to reliably build complex business processes and integrate separate systems together with more advanced messaging techniques.
Was This Helpful?
If you enjoyed this, please share on Twitter, Reddit, etc. so more devs can benefit from the content ๐.
Keep In Touch
Don't forget to connect with me on:
You can also find me at my web site www.jamesmichaelhickey.com.
Navigating Your Software Development Career Newsletter
An e-mail newsletter that will help you level-up in your career as a software developer! Ever wonder:
โ What are the general stages of a software developer?
โ How do I know which stage I'm at? How do I get to the next stage?
โ What is a tech leader and how do I become one?
โ Is there someone willing to walk with me and answer my questions?
Sound interesting? Join the community!
Top comments (14)
In most scenarios I would rather modify config adding threads/processes and/oo add servers than take risk and waste my time heavily modifying legacy code such as converting to async unless there is plenty of time planned and it's worth to pursue this undertaking. Computers are not people like chefs in kitchen they don't cost a lot.
Actually, it wouldn't be hard at all. I actually had already changed TONS of other places in the same codebase to use async. It's just an unfortunate reality that sometimes people jump the gun without considering what the root cause of an issue is and how that can be solved well - and that other people have the knowledge to make the right change.
In web apps, you usually cannot simply add "more threads" since the thread pool is a static size. If you have to add more (some pools allow that), then something more fundamental is probably very wrong with the app in question.
There's also the issue of improving code quality overall (which, for most legacy systems, in particular, I would say is a win).
So I guess I disagree ๐
I don't disagree with your. I'm sure having details of situation you made correct decision. You mentioned it was legacy application but from your comment it sounds like it's actually actively maintained application without too many problems I wonder makes it "legacy". Upgrading some old applications to async can be very painful. For example it might require upgrading legacy libraries to more modern ones that will have radically different interfaces (http, database drivers, ORMs, etc). If thread locals were in use this will need to be upgraded to different approach. If transactions are in use then db connection needs to be locked to async context. If code is littered with long-spanning transactions then this first needs to be broken down before considering upgrade to async flow otherwise async will bring no benefits. These are just some things I would expect to find in legacy application.
Static pool size is usually configured through config that what I meant by saying "adding more threads" sorry if didn't made it clear.
I think you've made a lot of good points there. Some legacy apps are really messy, tangled and use some poor practices.
I suppose it would also depend on how central such a system is to a business. Does it represent the "core" of the business?
If so, I would think it's worth more to invest time into improving the quality.
If not, or it's just not being maintained, etc... then perhaps not so much, as you said
I would agree, that in the cases you described it's really hard to add async stuff ๐.
I think perhaps where we disagree is about how often these near un-maintainable applications appear? I really wouldn't know what the overall percentage is...
And in some cases, we aren't talking about adding async, but just code quality in general.
I guess it's all about trade-offs. Is there a real benefit that will save the company money long-term?
I'd say we're both right - it just depends!
Thanks for the comments! ๐
Always enjoy your well-written posts James.
For the real-world references/analogies, it might be useful to reference that design patterns actually came from the architectural field, coined by Christopher Alexander in his "A Pattern Language" book. Things like "Facade" patterns suddenly start making sense :)
en.wikipedia.org/wiki/A_Pattern_La...
Thank you for your post, it is brilliant. I really enjoyed reading your series.
Thanks for the kind words Ramin! ๐
Fantastic blog post, I like how you compared software to physical world stuff (makes it very simple to understand when to use patterns). Thanks for sharing.
Thanks for the kind words Victor! ๐
Thanks, I am getting closer to senior dev and really enjoying design patterns and the benefits they bring. I would recommend "Clean code" to anyone researching this topic.
Welcome!
Even though Iโve read this article from a โjuniorโ devโs perspective, I enjoyed it very much!
Iโll reread this article in different stages of my career.
Thanks for sharing!
You're welcome!
You're welcome Dave! Glad you found this helpful ๐