My 5 Favorite Software Design Principles 

Jeremy Morgan - Aug 10 '19 - - Dev Community

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.

3. YAGNI

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.

4. KISS

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.

Conclusion

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

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .