OK, computer opinions time, Easter Sunday Edition™.

(Warning: I'm wearing my SRE hat and deploying your software to a war zone.)

--More--(4%)
My hope is that this thread serves as an OK starting point for anyone considering SRE engagement and a production readiness review.

I also hope that my example allows for some degree of extrapolation to, e.g., Django, http://ASP.NET  Core, etc.

--More--(8%)
I may consider delivering something like this tweet thread as a talk at some point. Let me know if you'd like to hear me prattle on about this kind of thing.

I have battle scars from this that I can probably talk about for days.

--More--(12%)
Anyway. Let's talk about Rails.

Specifically, let's talk about Rails' obsession with strong coupling to, say, Active Record. This has interesting implications not just for maintainability but also for things that I would consider important in production.

--More--(16%)
However, before we go too much further, let's lay down a foundation. What do I consider important in production?

--More--(20%)
As an SRE, I'm concerned primarily with observability and forensics.

Being able to observe a Rails app as it services requests to end users (or forwards requests to other services…) means that I may be able to analyze trends in metrics and predict failures.

--More--(24%)
On the other hand, forensics means that I can go in after the fact and stand a chance in divining why the app—forgive the pun—ran off the rails.

Usually, this is done through logs. More on that in a moment (and why the logging story for Ruby is kind of terrible)…

--More--(28%)
I'm also concerned with how testable a piece of software is, and architecting for observability and forensics has itself some interesting implications that make writing proper unit (read: fast, non-flaky) tests far, far easier.

--More--(32%)
Furthermore, this architecture effort has the additional benefit that development involves fewer moving pieces, which makes onboarding easier.

(Controversial opinion: Requiring developers to install an RDBMS, a key-value store, etc. on their workstations is dumb.)

--More--(36%)
Active Record's obsession with violating the single responsibility principle is a reliability liability.

Let's explore this.

--More--(40%)
The single responsibility principle states that a module or a class in a piece of software should concern itself with one aspect of the functionality of that software.

That's reasonable enough. I'll hold this as uncontroversial.

--More--(44%)
I'm going to assume (hopefully reasonably) that you're making good decisions in the design of your Rails app and are keeping your controllers "skinny."

This invariably leads to writing fat models, where the business logic is written in methods on the models.

--More--(48%)
Let's think for a moment what a single Active Record model does. (Or, really, what the pattern encourages.)

It wraps, e.g., a row of data.

It mediates access to, e.g., a database.

It validates that data against business logic.

Hmm, I count three here.

--More--(52%)
Even if we talk specifically about the gem over the pattern, without consideration for how the gem is often used, we're left with two responsibilities:

Encapsulating data in persistence; and

Mediating access to data in persistence.

--More--(56%)
Well, OK. How do we solve this problem?

Use the repository pattern (Fowler1 p.322). In conjunction with a bit of inversion of control, you write a class whose only concern is interacting with your persistence mechanism.

See https://gist.github.com/nrr/0a6ae872b0e49538d180d4baeea9e2e5 for an example.

--More--(60%)
Why is this important?

Well, imagine that you have a version of a similar service that routinely reaches O(10^a lot) req/s, and you're serving 500's because hot spots in your app cause your database servers to melt down. You have no idea where.

That's, um, bad.

--More--(64%)
In the tightly-coupled case, getting a handle on observability isn't exactly all that easy. Active Record models have a lifecycle that extends beyond simply requesting something from the database, and still, adding metrics to a model violates the SRP further.

--More--(68%)
In the loosely-coupled case as shown in the example, we can further instrument the repository by way of the decorator pattern (Gamma1 p.175) to emit the appropriate metrics to our monitoring system.

See https://gist.github.com/nrr/d068b057205b56a23f723cebc0430c24.

--More--(72%)
A similar decorator can be implemented to take care of logging.

A similar decorator yet can be implemented to guard against a thundering herd by way of a circuit breaker.

--More--(76%)
Yet another decorator could be added to handle caching your interactions with your RDBMS.

(And you can decorate that one with its own cache-specific metrics!)

--More--(80%)
Enough of that for the moment though. What about that last responsibility?

"It validates that data against business logic."

If not in methods on the model, where does the business logic go?

--More--(84%)
I already dropped something of a hint. You define a PORO with zero dependencies with methods that execute your business logic.

In our example, I was checking for obscene titles or content in our posts.

Your service layer (Fowler1 p.133) handles the other stuff.

--More--(88%)
The service layer is where you would, e.g., make sure that the session is authenticated before allowing access to unpublished posts. That is, the application-specific concerns.

Services can be decorated for metrics, logging, etc. just like repositories.

--More--(92%)
In addition, you can define a service for external web services (e.g., Stripe) that your app talks to and decorate it with circuit breakers and other graceful degradation patterns.

… and I guess that's it for now. Twitter wants me to stop here.

--More--(96%)
References:

[Fowler1] Fowler, Martin et al. Patterns of Enterprise Application Architecture. Addison-Wesley 2002.

[Gamma1] Gamma, Erich et al. Design Patterns. Addison-Wesley 1994.

--More--(100%)
You can follow @nrr.
Tip: mention @twtextapp on a Twitter thread with the keyword “unroll” to get a link to it.

Latest Threads Unrolled: