Building complex systems is never simple, but there are a few things you can do to make your life a bit easier. One of the hardest parts in designing these things is to make them testable, so that work tomorrow doesn’t break work today. I’ll go over a few concepts to make life easier when doing this.


The most fundamental aspect to system design is also the simplest: build your program as a composite of smaller programs. This doesn’t necessarily mean to separate your system across the binary domain by splitting it into communicating programs. You should apply the UNIX philosophy of “do one thing and do it well” to the components of your program.

You should heavily use the “Composition over Inheritance” principle. This principle suggests to “compose a class of instances of its behaviors rather than implement specializations of the class.” In practice, this behavior looks like this:

interace IUserService {
    User getUser(string id);

interface IEventService {
    Event getEvent(string id);

class EventController {

    IUserService _usrSvc;
    IEventService _evtSvc;

    View getEvent(string userid, string eventid) {
        if(_usrSvc.getUser(userid).HasPermission()) {
            return new View("Event", _evtSvc.getEvent(eventid);
        } else {
            return new View("403");

In this scenario, the implementations of IUserService and IEventService simply don’t matter as long as they fulfill the expected contract. This way, we can easily inject new behaviors into the EventController to handle new situations, like unit testing.

We also do this to reduce the complexity of both testing and maintaining our systems. Consider if we were to instead try to communicate directly with the database and write out a User object with it’s appropriate permission sets right from the getEvent() method. The complexity of this function would quickly grow to being difficult to update by a seasoned developer familiar with the code base. It would be practically impossible for a developer new to the project.