Service Swaps: Your Ticket to Clean Code

Think of your local transit agency as a dependency container. You just want to get from Point A to Point B, right? If the train’s out of commission, the agency swaps in a replacement bus, no problem. You don’t really care how they get you there, just that you arrive.

Disclaimer: Analogies are helpful, but they don’t always align perfectly. Let’s explore how this real-world scenario, despite its limitations, mirrors the powerful concept of dependency injection in software engineering.

Why

Software maintenance dominates engineering time, far outweighing new development. Simplicity is the key to manageable maintenance. Just as mechanics prefer Toyota’s straightforward engines to BMW’s complexity, engineers work more effectively with clean, well-structured code.

Dependency Injection makes software more flexible because components can be easily swapped out (like replacing that engine with a different one). It also makes testing easier since you can substitute real components with test versions. And maintenance becomes simpler because changes to one component don’t necessarily require changes to others.

How

Let’s get straight to the code

// Interface that defines our transit service contract
public interface ITransitService
{
    string GetTransitInfo();
}

// Implementation for train transit
public class TrainTransitService : ITransitService
{
    public string GetTransitInfo()
    {
        return "Train departing from platform 3 at 14:30";
    }
}

// Implementation for bus transit
public class BusTransitService : ITransitService
{
    public string GetTransitInfo()
    {
        return "Bus departing from bay 7 at 15:15";
    }
}

[ApiController]
[Route("api/[controller]")]
public class TransportController : ControllerBase
{
    private readonly ITransitService _transitService;

    // Constructor injection - the dependency is injected here
    public TransportController(ITransitService transitService)
    {
        _transitService = transitService;
    }

    [HttpGet]
    public IActionResult Get()
    {
        return Ok(_transitService.GetTransitInfo());
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);

        builder.Services.AddControllers();
        
        // Register the service implementation to use
        // Change this line to use TrainTransitService instead if 
// needed or this can be controller 
// via some external config/parameter which swaps this dynamically
        builder.Services.AddScoped<ITransitService, BusTransitService>();

        var app = builder.Build();

        app.MapControllers();

        app.Run();
    }
}

Keep it clean, keep it maintainable, and you’ll thank yourself later when you’re not spending your weekends debugging spaghetti code instead of enjoying actual spaghetti!

Now, go fix some bugs!

Circuit Breaker/Exponential Backoff Pattern (C#)

Electric circuit breaker device image

This device pictured above, part of all electrical systems(you hope so😀) has a feature to  put a break in electricity supply if something goes wrong.

WHY: Wherever there is overcurrent or short circuit, this prevents further damage to the whole system.

In software the current flowing through the network is the data. If one of the service your app depends on is down or slow, retrying it could cause more damage than solve the problem.

As part of making your application resilient and keeping the relevant services healthy, usually two patterns of retry are clubbed together. Circuit Breaker and Exponential Backoff.  In case of failure in service calls, following diagram explains how retry, exponential backoff and eventual circuit break looks like on a timeline.

Here it is re-trying a failing request four times, increasing the delay in calls each time, eventually giving up(break)

Alright, show me the code already…

An example in C# with Polly and Flurl

Don’t worry, you can copy or tinker the code here

Doing this just helped me wrap my head around it a bit better, production quality implementation will always look more detailed.

Real world example of adapter design pattern

Adapter is a structural design pattern that allows objects with incompatible interfaces to collaborate.

Nintendo Switch with its default interface works on a smaller portable screen but with an HDMI adapter it also connects to a TV.

C# representation of the same might look like…

Portable interface and its implementation

public interface IPortableDisplay
{
   Stream GetStream();
}
	
public class PortableGameConsole : IPortableDisplay
{
  public Stream GetStream()
  {
   return gameStream;
  }
}

Adapter code…

public interface IHDMI 
{
  Stream GetHDMIStream();
}

public class PortableConsoleHDMIAdapter : IHDMI
{
  private PortableGameConsole _portableGameConsole;
		
  public PortableConsoleHDMIAdapter(PortableGameConsole portableGameConsole)
  {
     _portableGameConsole = portableGameConsole;
  }
		
  public Stream GetHDMIStream()
  {
    return _portableGameConsole.GetStream();
  }
}