Code structure and layout
Yesterday, we talked about documentation and laying out a plan for creating new projects by first writing what you want to carry out. I started using this strategy recently and have noticed a massive improvement on my productivity. The impact is that I can focus on high level decisions and think about the functionality later. Today, I want to talk about structuring your code in an easy to manage format. I want to talk about how I approach the structure of my code. There is always an important consideration… When is being modular too modular?
So we have decided to start a new project. Now, a lot of our structure depends on what language will be primarily used. It is very easy to start with a flat directory structure and over-time refactor into an easier to manage format. For example, some of you might be writing PowerShell or Python scripts while others are making the next big thing in cloud management using Ruby on Rails or NodeJS. Regardless of the project, I find that it is extremely helpful for me to start thinking about the components of my application. I typically think about the following structure – Libraries, Vendor, Resources, Unit and Integration Tests, and Views/User-Interface.
Don’t get lost in your code libraries
Libraries:
I like to think of these as the common components or cogs that I will use in my project. Nine times out of ten my libraries end up being helper functions or custom framework modules leveraged by two or more of my Resources. The idea is to extract the code that you need to share into separate Modules/Classes that you can call. This makes my life easier when troubleshooting cross resource tasks.
Vendor:
Sometimes, I need to include an external library or modules as part of the code distribution. It is always a good idea to separate this from your custom code. It means that I can glance at dependencies easily and generally keep the code clear.
Resources:
To me, a resource is the engine of my code. Each time that I begin writing new functionality, I start by asking ‘Should this be its own resource?’. Consider the rule of thumb that I use – Write a resource for any new Classes or Models. This could be translated to scripts where we consider the backend modules as all the functions required to make that feature exist on its own. Remember, create a Library to share code with two or more resources.
Unit Tests:
Here is another one to consider. I know some developers that merge their unit and integration tests into a single directory and call it a day. This is completely doable but I typically keep them separated. I do this for a simple reason – convenience. I know that anything in tests/unit … well that isn’t hard is it? I do typically go one step further though. I will create sub-directories for my other components e.g. Resources and Libraries. I like to split my tests up into a very easy to manage process. Most test suites can just read the top-level recursively so it doesn’t cause any issues but it is sure handy when I need to find a bug in a new resource.
Integration Tests:
I follow a similar approach that I use for Unit Tests but with one major difference. Remember, Unit Tests confirm that conditions will trigger portions of the code. Integration tests confirm the expected behavior of the code. I typically focus my integration tests not on the resources and libraries but rather my views and user interface. More importantly, I want to test the results as if a user were executing the code.
Views and User–Interface:
As I think through my project, I am normally writing something that someone or something will interface. This code normally has little or no logic and is really only focused on simplifying the experience for the user. As developers, we often forget about who uses our code. Heck for most of us that do a lot of scripting (probably 90% of what I do anymore could fall into this category), we just assume that we will be the ones using it or our elected Padawan. This fact doesn’t change that it is a critical core part to just about anything that we do. I like to move these files into their own directory again for convenience and simplicity. You know that those files are the ones that you will deal with as you leverage the code.
There are many others that we could consider as well but I think that I will throw one more in there – Build. I sometimes need to distribute all of this code in an easier to consume model and defining my code structure may not always be best for the client. In these cases, I will write a ‘compiler’ aptly found in the bin directory to merge each of the product files into one or two files. This makes distribution tidy and I get to keep my repository nice and clean and simple to grow.
I think that we are off to a good start. Thanks for taking the time to join my on Day Two of this Lunch & Learn series. Tomorrow, we will jump into designing for tests and how to get into the habit of adopting a Test-First development pattern.