I was ready to ship my first real feature.
Editor open. Tasks clear. Momentum high.
Then I stopped, closed that branch idea, and spent the session setting up CI instead.
In the moment, it felt like I was delaying progress. There were no users yet, no incidents, no red alerts. But I had seen the opposite path before: skip CI early, move fast for a week, then slowly lose speed to noisy PRs, inconsistent checks, and “works on my machine” friction.
Not one big disaster. Just constant drag.
So this time I treated CI as infrastructure, not polish.
The day-one baseline I actually shipped
I kept it intentionally small:
- one lint job
- one test job
- same Python/tooling assumptions in local and CI
No matrix builds. No release automation. No deployment steps.
Just one non-negotiable rule:
If checks fail, nothing merges.
This was the exact baseline workflow I shipped on day one:
name: CI
on:
push:
pull_request:
branches:
- main
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v5
- name: Install uv
uses: astral-sh/setup-uv@v7
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.13"
- name: Run Ruff
run: uv run ruff check .
test:
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v5
- name: Install uv
uses: astral-sh/setup-uv@v7
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.13"
- name: Run pytest
run: uv run pytest
That was enough to make quality objective from day one.
The first run passed — and that didn’t make it optional
My first run went green immediately.
First CI run: both baseline checks (lint and test) passed. The point wasn’t complexity — it was installing merge guardrails from day one.
Past me would have used that as evidence CI could wait: “See? Nothing broke.”
First CI run: both baseline checks (lint and test) passed. The point wasn’t complexity — it was installing merge guardrails from day one.
But that was the wrong lens. The value wasn’t catching a bug on day one.
The value was removing ambiguity before complexity arrived.
Without CI, quality becomes social:
- “I ran tests locally.”
- “It passed for me.”
- “We’ll clean that up in the next PR.”
With CI, quality becomes mechanical:
- pass
- fail
- fix
- merge
Fewer opinions. Less friction. Cleaner reviews.
The assumption I had to unlearn
I used to think this sequence was efficient:
- Build features first.
- Formalize quality later.
In practice, “later” came with interest:
- Conventions drifted before they were written
- Test habits became inconsistent
- Cleanup PRs grew bigger than feature PRs
- Review energy went to mechanics instead of architecture
It looked faster early and got slower every week after.
The biggest cost wasn’t bugs. It was momentum decay.
What changed after installing CI first
The technical setup was simple. The behavioral shift was massive.
I wrote commits with merge gates in mind from the start.
I reviewed changes with less noise.
“Done” had a stable meaning.
Even as a solo builder, this mattered more than I expected. CI became my second pair of eyes: consistent, fast, and impossible to forget.
And because the pipeline was small, maintenance stayed cheap.
Why this matters on small codebases too
CI is often framed as “team process.” I now think that’s backwards.
Small projects change rapidly. Early habits harden quickly. If standards are vague in week one, that vagueness leaks into architecture decisions and review culture.
A tiny pipeline early prevents that drift before it starts.
You don’t need enterprise-level complexity.
You need a clear baseline that no one has to debate.
What I’ll repeat every time now
If I start another codebase tomorrow, I’ll follow the same order:
- Scaffold
- Add lint + test CI
- Enforce checks on merge
- Then accelerate features
Not because it looks sophisticated.
Because it protects momentum when the project gets real.
The lesson wasn’t “CI is useful.”
The lesson was timing:
The best moment to install guardrails was before I felt the crash.
Takeaways
- Day-one CI is not overengineering; it’s anti-chaos insurance.
- A minimal lint+test pipeline is enough to prevent early process drift.
- A green first run is still a win: it means your standards are in place before complexity compounds.
If you want to follow the build-in-public journey:

Top comments (0)