I just finally released Sustainable Web Development with Ruby on Rails after a year of writing and editing. https://sustainable-rails.com  Here are all the hot takes it contains: 1/59
Sustainability is like scalability, but harder to straw man. Sustainable development is literally development that can be sustained. Thus, your entire goal is to manage carrying costs. 2/59
Managing carrying costs, thus achieving sustainable development, is done by making design and architectural decisions, along with being strategic about what opportunity costs (technical investments) to incur. 3/59
A carrying cost is a cost you pay all the time every time. Your test suite is a carrying cost, as is your Webpack config. "Technical debt" is better thought of as a carrying cost: it's the cost of adding new features in code that's not as well-factored as it could be. 4/59
An opportunity cost is a cost you pay to do 1 thing instead of another. You can only do so many things, so when you choose to eg covert from Angular to React, that's an opportunity cost. There are other valuable opportunities you aren't taking in order to do this conversion. 5/59
Both of these concepts are critical to making engineering decisions. You are never deciding "the best tool for the job". You are almost always deciding if you can afford the opportunity cost and bear the carrying cost. 6/59
The choice to use Rails is a very smart opportunity cost and manages a lot of carrying costs. The choice to roll your own framework with e.g. Go or Node is a large opportunity cost with a larger-than-Rails carrying cost in many situations. 7/59
The key to managing the carrying costs of Rails is to treat Rails for what it is. Treat Rails for how it works and what it does, and try very hard to avoid making it something it's not. And, of course, use only those parts of it you need. 8/59
With that out of the way, hottest take first: business logic does not go in Active Records. Business logic. Does not. Go. In your Active Records. They are for accessing the database and are not models of anything other than a database table and THIS IS FINE AND GOOD. 9/59
Just look at the Active Record API: it's an API for accessing a database. And it's great at it! It's not an API for domain modeling, DDD, or whatever else you might think of as an according-to-Hoyle model. I know they are in app/models—they model database tables. It's cool. 10/59
The free chapter has the details, but your Active Records are often central to everything your app does (e.g. User class), so why would you put unstable, bug-attracting, essentially complex business logic on that class? You would not. 11/59 https://sustainable-rails.com/#outline 
Or, think about it this way: anything you can do in a method of an Active Record, you can do in another class that has an instance of an Active Record. So what value is putting code in an Active Record? Very very little value. 12/59
Next: `rails new` is good, not great. Use the env for secrets, not yaml files. Make a great bin/setup, bin/run, and bin/ci even if all they do is wrap one line of code. You'll be happy to have bin/run instead of a bunch of out-of-date crap in the README that no one reads. 14/59
This gets to an opportunity cost that seriously manages carrying costs: instead of documenting, automate things. Your code that everyone runs daily won't be nearly as out of date as your wiki. Also, no one can find anything in your wiki. 15/59
Use resourceful/restful routes always, but use `only:` so that bin/rails routes is accurate. If you need custom routes you probably need a custom resource. The harmony from browser to controller manages carrying costs. That's why it's there. 16/59
Exposing instance variables to templates is the poster child for the power of tight coupling and you should not route around it. That said, expose only one per controller action* 17/59

*except for reference data e.g. list of country codes
Just use ERB. No project ever failed because they didn't use Slim. Slim and HAML don't solve a real problem and create inconsistency (no need for end…sometimes). They don't abstract anything—they translate and so must you every time you write code. WHY would you want this? 18/59
Get a handle on your CSS. TBH you should use a utility CSS library like Tachyons or Tailwind. Semantic CSS is a lie and a sham and the way to an append-only application.css that everyone hates. BEM and the like require constant naming decisions and make re-use difficult. 19/59
Use server-rendered views. Your app does not need fancy JavaScript. Your users' 4 year old Android phone is not a great platform for rendering HTML. JavaScript is a massive liability and you should minimize it. 20/59
A "liability" is just a thing you are responsible for. Your code is a liability but JavaScript is a much larger one. It creates risk and carrying cost. Managing carrying cost is critical to sustainability. 21/59
That said, you will need some JavaScript, so manage it carefully. Try not using a framework. Browsers today are powerful. If you must add a framework, add exactly one and it probably should be React (though Hotwire looks very nice). 22/59
In the world of JS, your biggest enemy is instability and churn, thus picking highly popular, highly supported, broadly used libraries is even more of a factor than usual. "Best tool for the job" HAS GOT to account for the ecosystem. 23/59
Write a system test for every important thing your users do with your app. These tests mitigate the risk of breaking things that make your app work. Unit tests can cover edge cases and other stuff. 24/59
Don't forget that tests are a technique to mitigate risk. They aren't some moral achievement of sainthood. You can mitigate risk in a lot of ways. Test are a relatively cheap way to do it, but if a test isn't cheap, it's OK to question writing it. 25/59
Let's talk about comments. Every line of configuration you change—and this includes Gemfile—should be preceded by a comment explaining what is happening and why. Config is confusing and rarely visited, so it's the perfect place for comments. 26/59
Even better is to capture the conditions under which the change was necessary. Hell, check the version of some gem and if it revs, fail the test suite until someone sees if a bug you are working around was fixed. 27/59
As mentioned, Active Records are a great tool to access a database. If you need to expose data to a view or something, and you like a lot of what Active Records do, Active Model has you covered. Your favorite presenter framework does not. 28/59
The great thing about presenter frameworks is there's always room for one more in your app. Just don't. Not even once. Active Model is fine and plays well with Rails' internals and other magic. 29/59
Use the full power of your database. Foreign key constraints, check constraints, NOT NULL. The database is the only system which can absolutely ensure data integrity. Rails validations absolutely cannot (and that's fine). 30/59
Understand the difference between a logical model—what you use to build consensus about your domain—and the physical model—how you store that in the DB. They are not often identical and this is fine. Rails migrations manage the physical model. 31/59
This is a concept that trips developers up. The way in which a user perceives the domain may not be the best way to store data in a database. The logical model captures the user's view, but the physical model is for the programmer to reliably and unambiguously store data. 32/59
Validations are a wonderful tool for ensuring that users properly fill out web forms. Perhaps the best tool! They are in no way a mechanism for ensuring data integrity. Rails has a documented, public API that allows you to circumvent validations. 33/59

https://naildrivin5.com/blog/2015/11/15/rails-validations-vs-postgres-check-constraints.html
What about scopes? Scopes are a way to abstract a WHERE clauses when you need reuse. You should not put every single WHERE clause in your Active Records as a scope. The `where` method is a public API for you to use where you need it. 34/59
Scopes are just methods that you HOPE returns an ActiveRecord::ConnectionProxy. Hope might be what rebellions are built on, but it's not how to make a sustainable system. You know what always returns a ConnectionProxy? where. where should be your new best friend. 35/59
Scopes are not good for business logic. User.ready.chronological.available.signed_up is an absolute nightmare to deal with. If you need to query whatever the heck that is, put it in a class that has a name. You don't need a scope for this and I promise you'll creating one. 36/59
Another thing that trips developers up is over-designing for re-usability. This is the fundamental lie of object-orientation as told to us by uncles and manifesto-signers. A scope on a model might seem great for reuse, but if you don't reuse it, it's just a carrying cost. 37/59
Wait…what about callbacks? Don't we need a whole thread on the evils of Active Record callbacks? I will share a secret - when you use Active Records to access the database, and put business logic elsewhere, you don't need a lot of callbacks, so this problem solves itself. 38/59
Controller callbacks? use sparingly. Why do you want the code for your controller action to be spread across a bunch of implicitly called methods just to avoid a single line of stable code to assign an instance variable? You don't. 39/59
Active Job seems great, but it doesn't alleviate you from having to deeply understand how your background job system works. Active Job also requires you to think deeply every time you invoke it: does the parameter support global ID or not? 40/59
The behavior of your app will be easier to predict if you use your job back-end directly. Like HAML, Active Job isn't an abstraction, but a translation layer that doesn't provide a lot of added value to app developers (super useful for library authors, tho). 41/59
And yes, you MUST understand how your job backend works. What happens when a job fails? What happens when a job is retried? What happens if us-east-1 goes down before a job has been executed? Active Job will not help you with ANY of this (and that's fine). 42/59
Let's talk about security. First up, authentication. Do your users all use a trusted third party like GSuite? Use OmniAuth for authentication. They don't? Use devise. Don't roll your own authentication. 43/59
For authorization—who can do what in the system—use cancancan and base everything off of the canonical actions on resources. Always. Your authorization configuration should be designed to be easy to predict and easy to AUDIT. 44/59
A security person who does not know Rails will need to understand the security model of your app. If they can map from routes to actions to authorizations without learning Rails, everyone wins. Security audits are a carrying cost. You manage them with explicit simple code. 45/59
Update your dependencies OFTEN. Run bundle audit with EVERY build. Run yarn audit, too. Set up Brakeman. Any security issue fails CI and should be fixed ASAP. If you do all this multiple times a day, it's not a burden to fix these issues. 46/59
Being up-to-date on dependencies also helps with hiring. Would YOU want to work somewhere that's stuck on Rails 4.2 and Ruby 2.3? Unless you are a highly paid Rails Rescue Consultant, the answer is "no", you would not. 47/59
That said, NPM modules and security is a nightmare, which is another reason to keep your JavaScript footprint low. https://naildrivin5.com/blog/2019/07/10/the-frightening-state-security-around-npm-package-management.html
48/59
Don't let observability startup owners shame you: logging is super powerful and it's a technique that almost anyone of any level of experience can understand and be successful with. It's also cheap. It's just a start, but you should log more than you do. 49/59
Rails logging is a shit-show. Install lograge which makes things a bit better. You may want to make your own logging API. I hate to say it, but log4j circa 2003 is light years beyond anything in Ruby or Rails land. Still, logging is pretty great. 50/59
That all said, the startup owner is right that you should monitor business outcomes. No one cares what your CPU utilization is. Understand the difference in telemetry and a business outcome. You may need to design your app to have its business outcomes monitorabale. 51/59
Observability-driven-development isn't just a buzzphrase. You know those test we talked about? Observability is another technique to mitigate risk and it's often better to allow edge cases to go unhandled and monitor for them. 52/59
You want to make an API? Guess what: to_json is your API DSL MegaFramework. By implementing as_json on your Active Records and Models, you can easily generate API responses in controllers with to_json and use all the rest of Rails like normal. 53/59
"But Dave, why are we just exposing our database as an API?" I don't know, but if you can, you should because consistency is super great and it means that if you understand the database model, you understand the API. You can change it later. 54/59
What about microservices? Don't we NEED those? Probably you don't. You might actually get really far sharing a database. I know, there's a microservices book that says not to do that. But remember carrying costs. 55/59
Lots of Rails developers understand an app that uses a database. With multiple apps sharing a database, carrying cost is low…to a point. Microservices has a large carrying cost, and the transition is a huge opportunity cost. You should not pay it when your team is small. 56/59
Parting thoughts: treat Rails for what it is. Use Rails for how it actually works. Don't use every Rails API just because it exists. No framework will ensure clean code—you have to. Technical leadership is more important than controllers. https://sustainable-rails.com  57/59
Coda: I have not used Action Cable, Active Storage, Action Text, or Action Mailer. My only thought is that you will need to know deeply how these work because they are not widely used and you may be on your own to debug them. That might be a worthwhile tradeoff! 58/59
Super Coda: Just use @heroku. The amount of carrying cost teams incur by using AWS to host a Rails app is, well, large. Heroku is great. 59/59
About buying the book: you can do that directly: https://sowl.co/boqdo7 

If you are NOT from the US, I have arranged hopefully more equitable pricing: https://sustainable-rails.com/#pricing  so consult that before buying so you don't overpay.
There is a paper version on Amazon: https://bit.ly/sustainable-rails-print It uses LaTeX and, if I may, looks wonderful. Someday I'll tell you about the toolchain that produced it :)

Thanks for reading and keep using Rails!!!

<END OF LINE>
You can follow @davetron5000.
Tip: mention @twtextapp on a Twitter thread with the keyword “unroll” to get a link to it.

Latest Threads Unrolled:

By continuing to use the site, you are consenting to the use of cookies as explained in our Cookie Policy to improve your experience.