DEV Community

Cover image for My 5 Favorite Software Design Principles 
Jeremy Morgan for Pluralsight

Posted on

My 5 Favorite Software Design Principles 

I'm looking for honest feedback on my content. Please comment or reach out to me on Twitter!

Building software is easy. Building well designed, good software is hard. Very hard. I've been writing code since the 90s, and first got paid to do it in the early 2000s. I'm still learning, and always will be. 

While many will argue the nuances involved, there's a big difference between a software developer and a software engineer. The progression is usually:  
Student/Hobbyist -> Developer -> Lead Developer -> Software Engineer -> Software Architect.

If you want to advance in your career, you will need to learn good software design. Even if you're not concerned with advancing, you can start learning good design principles. No matter where you are at in your career this will make you a better developer. 
Want to get started right now?

Your future you will thank you for taking these steps. In this process, you'll learn a lot of principles for designing good software.   

Here are 5 of my favorite software design principles that have really helped my career. It not only helped me build better software but helped me understand the craft and relieve a lot of frustration. 

1. Abstraction

Abstraction is removing details from a process. For example, let's say you want code gets the area of a circle. You create a function called GetAreaOfCircle() and inside that function; you take the values, do the calculations, and return the area.   
You don't multiply pi times the radius squared every time in your code right? You do it once in that function and call the function. You've now abstracted that process. Anyone who uses your GetAreaOfCircle() function from here on out doesn't even have to know how it's calculated, they just use it.   
A good example of abstraction is something like Console.WriteLine(string) in C#. This function (or method) writes text to the console, but you don't know how it does that and you don't care. Database operations are often abstracted, and many other things. The .NET Framework is just a set of abstractions for developers. 

Why use abstraction? It makes your software reusable, and it allows you and other developers to focus on details that really matter. Do you really care exactly how an object is written to a database? No. You have bigger things to work on.

2. Separation of Concerns

Separation of concerns is a principle for breaking up your program into sections so each of them does a single thing. It's especially important in Object-Oriented Programming.
For example, do you want your database class to update the UI? Nope. Do you want your audio library to look at mouse input? No. This just convolutes things and adds complexity.   
Each of your classes should be a grouping of methods that are related to a single task. (Example: A user class that manages users) Each of the methods in that class should do one thing and do it well. For example, in your user class, have a method to add a user, another to delete, etc.   
Do one thing and do it well. 

Why use SoC? By separating things logically you make them more dependable, portable, and flexible. You reduce dependencies across your classes that make debugging a nightmare. Tight Coupling can make your life significantly harder.


You Aren't Going to Need It!! Programmers just love "stubbing things out" and building in code for "future expansion". I've done it myself. You add things you think you'll use later and have them waiting in the wings for their time to shine. This could be a disabled button or entire classes. Most of the time you end up with a bunch of code sitting there that will never be used. Resist the temptation. 

Why YAGNI? Classes and methods built out for future use are more harmful than extra space on the hard drive. It's one of the best ways to build up Technical Debt I know of. There's a cost to building it (your time), a longer delay (while you're building it and should be working on something else) and most importantly a cost of carry, which is all that complexity added into tests and debugging, with no payoff.


Keep It Simple Stupid! In the spirit of this principle, that's all that needs to be said.   
Why KISS? Complexity is the enemy. The more fancy and elaborate you make things, the more difficult they are to debug, and expand. Sure you can show off and build a remote control with 1000 features, but the user just wants to turn on the TV. Simplicity is far more impressive than complexity.  

5. DRY

Don't Repeat Yourself. This is closely related to abstraction. Don't repeat code anywhere. If you have to perform an operation twice and use the same code to do it, put it in a function (method). Some engineers will argue exactly how much something should be repeated before it's abstracted to a method, I say twice. If you have to do the same thing twice, throw it in a function. 

Why DRY? When it comes time to change things (which will happen) you have to change it each time you've repeated it. If you have an operation repeated and only change it one spot, you'll have unexpected results.
It also saves space when someone is reviewing or working with your code. Concise is better, always.


These are some of my favorite principles I've carried on throughout the years. As I'm designing and coding things they pop into my head like a reminder as I'm about to do something I shouldn't. This is a base set of principles that will help you no matter what your skill level. After a while, this will become second nature.   
Now that you've read this you're ready to start really building up your design skills.

Happy Coding!

If you have any feedback about this article, let me know in the comments or reach out to me on Twitter

Top comments (21)

nathanheffley profile image
Nathan Heffley • Edited

Great article! The only thing I have to say is that I'm one of those people who says that repeating something twice isn't enough to justify abstracting it out just for the sake of being DRY. In only the most simple programs will something that you use a third time be exactly the same as the first two times you did it. Waiting until you've repeated yourself three times helps you know what it is that you're actually repeating.

ssimontis profile image
Scott Simontis

Context is crucial can have the same code show up in different places with completely different contexts which looks repetitive, but it makes no sense to de-duplicate something that crosses system boundaries.

jeremycmorgan profile image
Jeremy Morgan

Absolutely right. Sometimes you can have the exact code verbatim and working off two different input/outputs. One of those implementations may change without the other so by de-duping and putting into a method you can be creating a regression.

This is why software engineering is so fun! (not saying it sarcastically)

Thread Thread
jsco profile image
Jesco Wuester

duplication is better than the wrong abstraction :D

Thread Thread
jeremycmorgan profile image
Jeremy Morgan

most of the time, yes

jeremycmorgan profile image
Jeremy Morgan

I can live with that. I usually stop and abstract when I find myself repeating something the 2nd time because I don't want it to spiral out of control. I've been criticized in code reviews for abstracting too much into methods, some of them only having one or two lines. I abstract things early which may be repeated... that arguably can be another form of YAGNI lol.

But I do like the "do one thing and do it well" philosophy. 3 times is reasonable.

jessekphillips profile image
Jesse Phillips

Yeah I was recently being told to go back and learn my basics in SoCs and stoics because I challenge the notion of blindly following patterns and abstractions.

Abstraction create layers and can lead to obfuscation if the wrong thing is being abstracted. This "right" thing can be extra hard to identify, so this is where YAGNI creates conflict with Dry or SoC.

phlash profile image
Phil Ashby • Edited

Welcome to level <n+1> :)

As you have neatly expressed, it's always a compromise between YAGNI and DRY/SOC. I like to aim for a sweet spot where it just passes the tests, and someone else (always yourself in 3 years time!) can maintain it without swearing at you!

twigman08 profile image
Chad Smith

Just to give even more advice to some beginners: don't focus too much on making sure you're hitting these things. Your first job is to write the software. Spending too much trying to make sure you're hitting all these can keep you from your main goal: finish the project.

Most of the time these things will just happen with experience. I mistakenly spent entirely too much time trying to meet one of these 5 principles before and it kept me from actually finishing projects. As you grow as a developer these things will develop naturally in your code most of the time.

carlillo profile image
Carlos Caballero

Hi Jeremy!

Thanks for your post.

I think that YAGNI and KISS are principles that you acquire with the experience because when you're novice you want to create complex software although your target was simple.

leocolman profile image
Leonardo Colman Lopes

Adding to that, when you're starting you believe that complexity somehow decreases your chances for bugs, improves your code quality and things like that.

I believe that many times that's not necessary. And I believe that many senior developers don't grow out of that phase, and keep trying to apply ALL concepts and ALL abstractions ALL THE TIME!

jeremycmorgan profile image
Jeremy Morgan

It becomes a giant feedback loop with some people. You create a bunch of complexity in the beginning, learn more and lean it down, then learn more and make it complex again trying to fit some neat pattern you read about. I've been guilty of it myself.

Sometimes you have to step back and ask yourself "Am I shipping anything? Why not?"

erebos-manannan profile image
Erebos Manannán

There is no difference between "software engineer" and "software developer" other than that some organizations like one term over the other. AFAIK in some parts of the world the term "engineer" is also protected and thus "developer" is preferred.

jeremycmorgan profile image
Jeremy Morgan

While I agree many organizations abuse the terms, there is a clear difference.

It's similar to a bricklayer and the architect drawing up plans. While they're both essential to creating the building, each has their own craft to it. This is exactly why some people enjoy being developers and staying developers rather than move to the engineering side of things.

erebos-manannan profile image
Erebos Manannán

This is like arguing "hacker" is different from "cracker", might have been the case 30 years ago, definitely not so in the world we live in now. The security industry decided to find a new way to differentiate with the white/gray/black -hats.

I've worked in the industry quite a long time and never think a "Software Engineer" is in any way different from "Developer" when I see them in CVs/business cards/whatever. With the way people abuse the terms I barely see any difference between a "Junior Developer", "Developer", or "Senior Developer" .. hell some "Lead Developers" are basically people who learned to code 2 years ago on some online site and someone incompetent then hired them in their startup.

At least the difference between Developer, Architect, DevOps/SRE, etc. still mostly remains clear.

mdhesari profile image
Mohammad Fazel

Very specific and out-standing, thank you. I'm on my way learning more about abstraction and OOP principles, if you have advance and good source code that shows these principles clearly, I'd be glad if you share it with me.

jeremycmorgan profile image
Jeremy Morgan

I will be sharing a lot more examples of this an more in the coming few months on here. I imagine others will be joining in as well. Be sure to follow the Pluralsight page.

saisivanow profile image

I know why we need to learn Software Design principles. They let you build scalable, maintainable, and reusable piece of code. There by generating more value to organisation by reducing computing or human resources required to do a Unit of Task which would convert as a value for you.

jeremycmorgan profile image
Jeremy Morgan

Guilty. But lots of abstractions means faster running unit tests, right?

kleene1 profile image

Cool not sure on the extra code part ; I guess it's a case by case kind of thing.

codebrotha profile image
Tineyi Takawira

Do you really care exactly how an object is written to a database?

Oh, but I do care. lol

I get the point you're making though.