Rails Technical Due Diligence: A Fractional CTO Checklist for Acquirers and Investors
Rails technical due diligence checklist from a fractional CTO. What to audit before acquiring or investing in a Rails app — codebase, infra, team, risk.
A boutique PE firm in Amsterdam called me on a Tuesday last March about a Rails SaaS they were ten days from acquiring. The seller’s deck claimed €4.2M ARR, 28 enterprise customers, and “a modern, scalable Rails 7 codebase.” The price was €18M. The PE partner had two questions: was the technology actually worth what they were paying, and what was going to break in the first ninety days after close. I cleared my calendar, flew to Utrecht the next morning, and spent four days inside their staging environment, their GitHub org, their AWS console, and the heads of the four engineers who had built the thing. We closed at €13.2M with a €1.8M escrow for migration costs. That four-day Rails technical due diligence engagement saved the buyer €4.8M.
After nineteen years of building Rails systems and the last six advising acquirers, investors, and founders as a fractional CTO, technical due diligence is the highest-leverage week of work I do. The price you pay for a software company is set by a spreadsheet. The cost of owning it is set by what is actually in the codebase. This post is the exact checklist I use, in the exact order I run it, with the questions I ask and the red flags I refuse to ignore.
Why Rails Technical Due Diligence Is Different
A standard tech DD plays out like a financial audit: read the code, count the tests, tick boxes, write a report. That gives you a snapshot of the asset on the day you looked at it. It does not tell you what it will cost to operate, what it will cost to extend, or what happens the morning the lead engineer hands in their notice three weeks after close.
Rails technical due diligence has to answer four operating questions, in priority order: can we keep the lights on, can we ship the roadmap, can we survive the team turning over, and is anything ticking that will detonate in the first year. Everything in the checklist below ladders up to one of those four questions. If a finding does not change my answer to any of them, it does not go in the report. Acquirers do not pay me to tell them the indentation is inconsistent.
I also work differently from a code-audit shop. I am not grading the engineers. I am pricing a transition. The same codebase is a different risk profile depending on whether the buyer is keeping the founding team, bolting it onto an existing platform, or rebuilding it in two years. The checklist is the same, the verdict is not.
The Four-Day Schedule I Actually Run
I run Rails technical due diligence as a four-day fixed engagement with a five-day option for complex platforms. Day one is access provisioning, repo clone, and the senior engineer interview. Day two is codebase and infrastructure. Day three is security, compliance, and incident history. Day four is the team and the report. The five-day option adds a customer-impact assessment that pulls in product analytics, support volume, and the top three open tickets.
The deliverable is a fifteen-to-twenty-five page report with a one-page executive summary, a red/amber/green scoring grid across twelve dimensions, a costed remediation plan for everything red and amber, and a recommended price adjustment. The exec summary is the only page the deal partner will read. Everything else has to defend it.
The Twelve-Dimension Scoring Grid
Twelve is not a magic number. It is what I have converged on after running roughly sixty of these. Each dimension gets red, amber, or green, each finding gets a euro cost to remediate, and the cost rolls up into a single price-adjustment recommendation. The dimensions are: codebase health, test coverage and reliability, Rails and dependency currency, database design and performance, security posture, infrastructure and deployment, observability and incidents, third-party dependencies, IP and licensing, team and knowledge concentration, customer-impact risk, and roadmap feasibility.
I will walk through the heavy ones below. The light ones (IP, licensing) get a paragraph in the report if clean and a section if not.
Codebase Health: The First Hour Tells You Everything
The first hour I am alone with a repo is the most predictive hour of the entire engagement. I clone the repo, run bin/setup, and time how long it takes to get a green test suite on my laptop. If bin/setup does not exist or does not work, that is a yellow flag before I read a single line of application code.
Then I run a series of quick measurements I have automated into a small script. Lines of Ruby. Number of models. Number of controllers. Number of background jobs. Average controller action length. Largest model. Number of # TODO and # FIXME comments. Number of files that have not been touched in two years. Number of files that have been touched by exactly one author.
# script/dd_metrics.rb — run during due diligence
require "find"
stats = Hash.new(0)
single_author = []
Find.find("app", "lib") do |path|
next unless path.end_with?(".rb")
stats[:files] += 1
stats[:lines] += File.foreach(path).count
authors = `git log --format='%ae' -- #{path}`.split("\n").uniq
single_author << path if authors.size == 1
end
stats[:single_author_files] = single_author.size
stats[:largest_model] = Dir["app/models/*.rb"].max_by { |f| File.size(f) }
stats[:todos] = `git grep -c "TODO\\|FIXME" -- '*.rb'`.split("\n").size
pp stats
A healthy mid-stage Rails codebase has somewhere between 30,000 and 120,000 lines of Ruby, a handful of models above 500 lines, a single-author concentration under 25 percent, and a bin/setup that works on a clean machine. Anything outside those bands is a question, not yet a problem.
The genuine red flag is the single-author concentration. If 60 percent of the codebase has only ever been touched by one engineer, you are not acquiring a company, you are acquiring a person. That changes the entire deal structure. You need retention payments, you need a knowledge-transfer plan, and you need to model what happens if they leave in month four anyway.
Rails and Dependency Currency
I check four things in this order: the Rails version, the Ruby version, the number of outdated gems with major-version lag, and whether anything in the Gemfile is unmaintained.
bundle outdated --strict
bundle exec ruby -v
grep '^ruby' Gemfile
cat Gemfile.lock | grep -A1 RUBY
A Rails app two minor versions behind current is normal. Three is amber. Four is red and gets priced. A Ruby version more than one major behind end-of-life is red — you are now buying a security migration on day one. A Gemfile with more than five unmaintained gems (no commit in 24 months, no release in 18) is amber and gets a remediation budget.
I also look at the history of upgrades. A team that successfully upgraded from Rails 6 to 7 to 7.1 has shown they can do it again. A team that has been on the same major version since 2021 has not. The cost of the next upgrade is much higher when nobody on the team has done one. I price that in.
Database Design and Performance
This is where I find the most expensive surprises. Open a production-equivalent psql session and run a handful of queries: the largest tables by row count, the largest tables by disk size, the indexes that have never been scanned, the tables with sequential-scan-to-index-scan ratios above 10:1, and the longest-running queries in pg_stat_statements.
SELECT relname, n_live_tup
FROM pg_stat_user_tables
ORDER BY n_live_tup DESC LIMIT 10;
SELECT indexrelname, idx_scan
FROM pg_stat_user_indexes
WHERE idx_scan = 0
ORDER BY pg_relation_size(indexrelid) DESC LIMIT 20;
If pg_stat_statements is not enabled, that is itself a finding — they have been running this thing without query-level visibility. Turn it on for a week and come back.
I am looking for three patterns. First, a table over 100M rows with no partitioning and no archival strategy — that is a future incident with a known date. Second, the top ten queries by total time including any unbounded LIMIT-less scan — that is a memory and timeout problem waiting for a traffic spike. Third, missing migrations that the team has been hand-running on production through psql sessions — that breaks the audit trail and your ability to reproduce the schema.
Migration Safety and Deployment Risk
I read the last two years of migrations. I am looking for evidence the team uses strong_migrations or the equivalent, that they have ever done a zero-downtime migration on a non-trivial table, and that there is no migration in the history that would have locked a production table for more than 30 seconds.
# A migration like this in the history is a red flag
class AddIndexToOrders < ActiveRecord::Migration[7.1]
def change
add_index :orders, :customer_id
end
end
That migration has no algorithm: :concurrently, no disable_ddl_transaction!, and on the 80M-row orders table I am looking at it would have locked the table for 90 seconds. Either the team got lucky, or they took an outage and did not document it. I ask.
Security Posture
I run bundle audit, brakeman, and a manual scan for the usual Rails footguns: params.permit! in controllers, raw SQL with interpolation, protect_from_forgery exceptions outside the API namespace, to_s on user input rendered into HAML or ERB without escaping, and system or backticks called on anything derived from user input.
I check authentication. If they rolled their own, I read it. I check session storage. I check that secrets are not in the repo (git log --all -S 'AKIA', git log --all -S 'BEGIN RSA', git log --all -S 'sk-'). I check that the production database is not reachable from the public internet. I check that Rails.application.credentials is actually used and not a staging-only convention.
Anything secret that has ever been in git history is a red flag with a fixed cost — rotation across every customer and integration. If the codebase predates 2018 and uses ENV vars exclusively, I assume there has been at least one leak in someone’s dotfiles and price accordingly.
Infrastructure and Deployment
I want to see the runbook that says how a new engineer deploys for the first time. If it does not exist, the runbook is in someone’s head and we now know whose. I want to see how a rollback works, when the last rollback happened, and how long it took. I want to see the boot time of the production app — if it takes 90 seconds to start a worker, that constrains every deployment decision the buyer is going to make for the next year.
I check the CI pipeline. Time to green on a typical PR. Cost per month. Flakiness rate on the last 200 runs. A 45-minute green pipeline is amber and slowing the team down by maybe 20 percent. A test suite with a 15 percent flake rate is red and is silently teaching the team to ignore failing tests.
I check what is actually in production. The infrastructure-as-code repo should match what is running. If terraform plan against production has a 400-line diff, the IaC is not the source of truth and the team has been hand-editing the AWS console. That is a finding.
Observability and Incident History
The single best question to ask an engineering lead during DD is “walk me through your last three production incidents.” If they have to think hard, either there have been no incidents (improbable for a company with paying customers) or there is no incident process. Both are findings.
I want to see error rates in Sentry or equivalent. I want to see the top ten errors by volume and how long each has been firing. An exception that has been thrown 40,000 times a day for six months is not an error — it is a feature the team has agreed to ignore. That is a culture finding, not a code finding, and it tells you something about how the engineering org makes tradeoffs.
I check uptime history. I check the mean time to recovery. I check whether anyone is on call and how that rotation works.
Team and Knowledge Concentration
This is the most consequential section of the report and the one acquirers most want to skip. I interview every engineer for at least 30 minutes, not to grade them but to map who actually knows what. I am building a heat map of where knowledge lives. If three out of four engineers say “you would have to ask Marcus” when I ask about the billing system, Marcus is now a key-person risk and the deal needs a retention plan for him.
I also ask each engineer two questions: what is the part of the system you are most worried about, and what would you build differently if you could start over. Engineers tell the truth in these conversations in a way they do not tell it to founders. The compounded answers are the most accurate roadmap of the next two years of remediation work.
A useful related read on what to look for in a strong engineer is the senior Rails engineer interview rubric I use for hiring — many of the same signals apply when evaluating an inherited team.
The Report and the Price Adjustment
Every finding gets a remediation cost in euros and a priority — must-fix in the first 90 days, fix in the first year, fix when convenient, accept the risk. I total the must-fix bucket, multiply by 1.5 for execution risk, and that is the price-adjustment recommendation. The first-year bucket goes into an operating-cost projection that the buyer can use to plan the post-close engineering budget.
On the Utrecht deal I opened with, the must-fix bucket was €3.1M (Rails 5.2 upgrade, Postgres 11 upgrade, secrets rotation, replacing a homegrown billing system that was three months from breaking under EU VAT changes). The execution-risk multiplier brought it to €4.65M. The seller agreed to €4.8M off the price plus a €1.8M escrow. Everyone walked out fine. The PE firm is two years into ownership now and the platform has tripled revenue.
FAQ
How long should Rails technical due diligence take?
For a single Rails app under 200,000 lines of code, four working days of a senior fractional CTO is enough to produce a reliable report. Multi-app platforms, microservice setups, or apps with significant React or mobile front-ends typically need five to seven days. Anything advertised as a one-day audit is a checklist scan and will miss the things that actually move the price.
What does Rails technical due diligence cost?
A fixed four-day engagement in the EU typically runs €12,000 to €25,000 depending on platform complexity and how much access provisioning the engineer needs to chase down. That is rounding error against a €5M-plus acquisition and routinely pays back ten-to-fifty times in price adjustment or avoided post-close surprises.
Who should run technical due diligence on a Rails acquisition?
Someone who has personally shipped and operated production Rails for at least ten years, who has done at least five prior DD engagements, and who is independent of both the seller and any vendor the buyer might use for remediation work. A fractional CTO with operating experience produces a more useful report than a generalist tech-audit firm, because the report is graded on operating decisions, not on code style.
Can technical due diligence kill a deal?
Yes, and it should when the findings warrant it. Roughly one in eight engagements I run results in a recommendation to walk away or to fundamentally restructure the deal. The more common outcome is a price adjustment and a costed remediation plan that both sides agree to before close. The point is not to find reasons to say no — it is to make sure the buyer knows what they are buying.
Considering an acquisition or investment in a Rails-based business? TTB Software runs structured technical due diligence engagements for acquirers, PE firms, and investors. Nineteen years of Rails, sixty-plus prior engagements, fixed-fee four-day delivery. Get in touch before you sign the LOI, not after.
Related Articles
Build vs Buy Software: A Fractional CTO's Framework for Engineering Decisions
Build vs buy software decisions can sink a startup. A fractional CTO's framework for evaluating vendors, total cost, ...
Senior Rails Engineer Interview: A Fractional CTO's Hiring Rubric for 2026
Senior Rails Engineer Interview rubric from a fractional CTO with 19 years of Rails. The questions, scoring, signals ...
What I Do First When I Inherit a Legacy Rails Codebase
A fractional CTO's practical framework for the first 30 days taking over someone else's Rails app. Where to look, wha...