Something Never Changed!!! Guiding Principles to Software Development

Discover timeless guiding principles for software development that remain relevant today. Learn why fundamental coding practices never change and how to apply them.

8 min read 1,527 words

I am particularly a lazy guy. I don't work hard, stay back, and never show a sad face to an issue or bug. Obviously, many don't like me for this. But again, "who cares?". But one thing is sure for me: I'm always ready to learn new things and obviously always work for better code. At the end of the day, it's just code. Bugs will be there, and me too, to solve the issue.

Here, I mention new things, but in reality, it's not possible all the time that I will get new things. Sometimes I will get old things to take care of, or something that never changed. I was reading old articles from Hanselman. Nowadays, he is putting more preference on high-quality articles. So, his articles are not coming on a daily basis. So, one day I was looking through the hit series of articles, and I found "some guiding principles for software development". And again, as per suggestion from Hanselman, I read it later.

Now, I was working on something, and just to get refreshed, I was checking my Instapaper and found this one. I was reading it. The first thing I noticed is that it was written in 2007, even before I was a certified software engineer. As I normally don't read historical articles that much, but I read this. And that surprised me!!! Nothing has changed at all. Principles are the same even after 5 years from when this article was written. And when they wrote it, it was there already. And also one thing hasn't changed too—we are not following most of them… (speaking from personal experience). For a change, nowadays I've tried to follow them.

So, I thought I should rewrite this article in the context of current technology. I am also going to put links to tools, but this is my personal preference, obviously in the context of .Net only. I started learning Ruby on Rails, but again, I'm not that good at it.

The original posts are from Patrick Cauldwell and Scott Hanselman. Be sure to visit the original work.

  • Our "Guiding Principles"

  • Test Driven Development

  • TDD/BDD always gives confidence in our own written code. Couple of frameworks are available for that. For TDD, NUnit and XUnit are there. And for two types of BDD, Specflow and NSpec are there.

  • Continuous Integration

  • This includes testing for the complete application and also UI automation using frameworks like Selenium (visit Amir Rajan to check out wonderful videos on UI automation)

  • I prefer to check out blogs from Amir Rajan and podcasts from Hanselminutes to explore this topic.

  • Unit Tests

  • Two kinds of tests

  • Unit tests are tests that test code written by us, so it is not dependent on external entities like SQL or Web Services, etc.

  • Integration tests depend on SQL and Web Services like external entities and check our code in reference to our logic.

  • Automation is equally possible for both types of tests.

  • Again, I like to mention a post by Amir Rajan which shows how to do automation.

  • As a rule of thumb said by Hanselman, whenever we need to repeat things or even have to do copy-paste, we should automate things.

  • There are many tools available for Visual Studio to automate testing (you need to hack a little bit for Express Edition).

  • All UI development should follow MVC/MVP pattern for ease of testing.

  • MVC is specifically designed for better testing support.

  • Even if you are using ASP.Net Web Forms, I strongly recommend the MVP framework to implement support for more code coverage for testing.

  • Even for JavaScript, this is true, as many client-side frameworks are available like Jasmine and Qunit.

  • Test Coverage

  • 90% and above should be the goal, including UI logic written in JavaScript.

  • Again, many tools are available for code coverage, so I'm not listing them here. NCover is there by default in Visual Studio higher versions.

  • Buy, Not Build

  • Take full advantage of the platform, even if it only solves the 80% case. (Most of the time, we don't know library functions available, which leads to copy-pasting code and rewriting functionalities.)

  • Don't write a single line of code that you don't have to. (In software engineering, one has to be lazy. If you are staying back working, you're not writing good code.)

  • Take full advantage of .Net 4.0/.Net 4.5 and also plan and test for Windows 8. (We are working for the future, so it must work on future/current releases of platforms.)

  • Don't invent new solutions for solved problems. (In the case of .Net, NuGet is there. Just check that your problem is solved by community members. If yes, use it. If no, solve it and share with the community… These OSS libraries increase productivity like anything.)

  • Limit compile-time dependencies on code you don't own

  • Everything that is not owned by us should be behind an interface and a factory method. (I also like to mention a wonderful article series to understand concepts like interfaces.)

  • Define your data contracts in C# (think 'active records')

  • All persistence storage should be abstracted using logical interfaces.

  • Here we can use ORMs like Ormlite, Dapper, EF, and a few others to achieve this.

  • Fewer assemblies are better

  • There should be a very good reason for creating a new assembly.

  • The assembly is the smallest deployable unit, so it's only worth creating a new assembly if it means NOT shipping something else.

  • Namespace ≠ assembly name. Roll up many namespaces into one physical assembly if they all must be deployed together.

  • As a rule of thumb again said by Hanselman, domain-driven and/or isolated design is only useful when we have a VERY good knowledge of domains; otherwise, it just increases the number of assemblies to manage.

  • Only the public interface should be public

  • Only make classes and interfaces public if absolutely necessary.

  • Testing can be done by using internal visible to attribute.

  • Good use of internal can avoid public issues for classes and functions.

  • If it's public, you need to provide support for its lifetime.

  • Also like to mention a line from Jon Skeet: "Any software/application should be coded as if we are coding an API."

  • Authentication

  • Authentication should be abstracted from the application.

  • We can use WIF, and tokens should be used to talk with other entities like Web Services.

  • Version 4.5 framework (ASP.Net Framework) provides many options for Authentication/Authorization that can be used here.

  • Tracing

  • Think long and hard about trace level.

  • Use formatted resource strings everywhere for localization. (Nowadays, localization is a must for any commercial application. Abstraction of that always helps in the long run.)

  • For critical, error, or warning messages, your audience is not a developer.

  • Error Handling

  • Error handling is not just putting try/catch everywhere; that's what I call error hiding.

  • Method names are verbs.

  • If anything breaks the contract, throw an exception.

  • The definition of "Done" (or how do I know when a task is ready for QA?)

  • Any significant design decisions have been discussed and approved by the team. (In agile, the team takes the decisions, so there is always a scope for saying NO to business guys for new functionalities to implement at runtime.)

  • For each MyClass, there is a corresponding MyClassFixture in the corresponding test assembly. It also includes the same for the UI part using JavaScript framework.

  • MyClassFixture exercises all the functionality of MyClass and nothing else. Single responsibility applies to test classes too.

  • Code coverage of MyClass is ≥ 90%, excluding only lines you are confident are unreasonable to test.

  • No compiler warnings are generated by new code. (Warnings are equally important as errors. You can ignore them if you are doing POC or a college project. In Visual Studio, warnings are generated after completing code analysis. They should be on all the time, and personally, I prefer to set it so the project won't run if there are warnings.)

  • Before committing anything to source control, update to get all recent changes, and make sure all unit and integration tests pass.

  • Compiling with warnings as errors will flush out any place you forgot documentation comments, which must be included for any new code.

This is just an attempt to list our frameworks and new technology with these good old guiding principles. Do let me know if there is anything to add, change, or remove from this list.

This list is in my learn by heart section. Basically, these points force good software practices.

Frequently Asked Questions

What are the main guiding principles for software development that never change?

The core guiding principles include Test Driven Development (TDD), Continuous Integration, and comprehensive Unit Testing. These principles have remained fundamental to quality software development for over a decade and continue to be essential regardless of technology changes. Despite their proven importance, many developers still struggle to consistently implement them in practice.

What is the difference between unit tests and integration tests?

Unit tests focus on code you've written and don't depend on external entities like databases or web services. Integration tests, on the other hand, depend on external systems like SQL databases and web services to verify your code works correctly with those dependencies. Both types can be automated and are equally important for comprehensive code coverage.

Why is Test Driven Development important for software engineers?

TDD gives developers confidence in their own written code and ensures quality from the beginning of development. By writing tests before or alongside the code, you catch bugs earlier and create more reliable, maintainable code. This practice has remained a guiding principle for quality software development for many years.

What tools are available for implementing Test Driven Development in .NET?

For TDD in .NET, you can use NUnit and XUnit as testing frameworks. For Behavior Driven Development (BDD), SpecFlow and NSpec are excellent options. For UI automation testing as part of Continuous Integration, Selenium is a widely-used framework that works across different technology stacks.

How does Continuous Integration improve software development?

Continuous Integration involves testing the complete application and automating UI testing to catch issues early in the development cycle. This practice ensures code quality remains consistent and bugs are identified and fixed quickly. It works hand-in-hand with unit tests and automation frameworks to maintain reliable software throughout development.

Share this article