2 Martians, greenfield to MVP in 4 weeks: agentic coding on Rails

We shipped a full production MVP in 4 weeks with a team of two Martians: a designer and an engineer. In this post, we’re sharing the experience, lessons learned, the open source skills we developed, and why Rails + Inertia is now our go-to stack for agentic coding.
Thicket is an educational platform where PhD-level experts offer live classes in literature, history, art, and other subjects for curious learners. Founder Nick Constantino brought the vision and mockups we needed to get something real in front of a teacher within days rather than weeks. We needed to find a way to make it happen.
Zooming out a bit for context, Evil Martians ultimately chose Inertia + Rails + React in a single repo, and Storybook as the design environment. This setup gave the AI strong architectural constraints and made it simple to add skills for encoding project-specific decisions. It was the perfect setup for our Martian Designer and Engineer to collaborate and deliver so effectively that Rails + Inertia is now our go-to stack for agentic coding.
If we started another project like this today, we’d use the AI harnesses developed while working on this project from day one. We know, because we built the foundation—and turned what we learned into open source!
By the end, the full feature surface included: authentication and onboarding flows, Stripe Connect payments for teacher payouts, live video classes via Whereby, a course builder, dedicated teacher and student experiences, a teacher application portal with admin review, a blog, and an admin panel.
When a Stanford professor used the product for the first time, she said it was the best teacher experience she’d seen on the market.
To get real user validation fast, week one ended with a real teacher using the product. We skipped building a prototype, skipped Figma walkthroughs, and started with a working platform.
Phase one: speed to signal with Bolt.new
Our Martian designer, Gleb, started in Bolt.new. Instead of drawing screens in Figma and handing them off, he was designing directly in React. He would prompt, watch Bolt generate a working interface in the browser, then refine in real time. The mockup was a running app, and this meant we were successfully able to get a real user interacting with the product by the end of week one.
Bolt.new was awesome for quickly turning an idea into a live prototype. Here is just a selection of some real user quotes from this phase:
- I found everything really intuitive. It’s funny, because as we were just going through it I was like… I want it to be up now! I want to put a course on here. Going through it was super easy, it made a lot of sense. - Deanna, Teacher Tester.
- It’s quite simple, in the best way. I mean that as a compliment, that it’s simple. - Tim, Student Tester
- Excellent—I’m gonna be on the list! - Carol, Student Tester
But because the initial POC was created in a greenfield, design-first way bringing it into a production app required significant manual refactoring:
Initially, the workflow was cumbersome. We were essentially maintaining two systems. The frontend was built in Bolt using React, which couldn’t run Rails, so I had to manually port everything to Inertia Rails and keep the two synchronized. It felt like constantly translating between separate worlds.

Svyatoslav Kryukov
Backend Engineer
Phase two: Inertia and speeding to production
So, while the prototype environment was great for fast exploration, it had no awareness of production Rails architecture, real data flows, or Inertia conventions. That meant every new frontend iteration also created extra translation work.
To keep moving, we needed the designer working inside the same system as the engineer, sharing one Rails + Inertia + React codebase, with the same patterns, constraints, and conventions. This led us to Claude Code, which changed the shape of the rest of the project:
Claude Code changed the dynamic entirely. Gleb and I could work on the frontend without that back-and-forth porting layer. And as newer models came, we built project-specific skills. For instance, to handle N+1 queries with the
ar_lazy_loadinggem instead of chasing micro-optimizations everywhere. Eventually, manual intervention dropped to almost zero. Just smooth, collaborative work.

Svyatoslav Kryukov
Backend Engineer
Our agentic coding stack: Inertia, Rails, and Storybook for design
Evil Martians chose Inertia + Rails + React in a single repo, and Storybook as the design environment.

Irina Nazarova CEO at Evil Martians
Rails conventions give AI coding tools a structured, opinionated surface to work within: how models relate, how controllers behave. Predictability simply makes AI-generated code more reliable. In short, Rails is the right stack for agentic coding.
Meanwhile, React is the lingua franca for AI coding tools. But going full React without an API layer means reaching for Inertia; this keeps you close to the Rails monolith while giving the designer and the AI a React surface to work on.
Working in a single repo with Storybook changed how the designer and engineer collaborated entirely. The designer visualizes components exactly. They can then build the flows exactly how they envision users moving through the product.
From there, the engineer takes this code directly and polishes it: fixing non-canonical AI decisions, adding abstractions where necessary, and making it maintainable.
In an alternate workflow, Figma might introduce a blur between design intent and production reality; in other words, an engineer has to re-interpret and re-implement design intent. Here, the designer ships the code the engineer starts from. This is precisely what made this two-person designer + backend engineer team viable.
AI needs a prompter, not just a prompt
A fresh Rails project is actually where agentic coding works best. Rails conventions are mature enough that the AI follows them reliably, and you can go quite far without manual steering.
AI is like a junior developer without the professional vision and pattern recognition built from years of experience. It sprints confidently in one direction. But if the architecture needs to pivot, it won’t notice.

Svyatoslav Kryukov
Backend Engineer
The drift that plagues greenfield projects on less opinionated stacks is largely absorbed by Rails itself. Where you do need to take the wheel is at the frontier. Here, AI reaches for outdated Inertia idioms because that’s what it was trained on.
And on this project, we weren’t just using current Inertia—we were building on top of it: alba-inertia, improvements to typelizer, an Inertia Puma plugin, Inertia infinite scroll features, and more. None of that existed in the AI’s training data.
The birth of a skill: the filter abstraction story
Several skills were born from this project. The open source that came out of Thicket is now what everyone can use for their greenfield project. (Every project makes the harness smarter.) You can check out the layered Rails skills, the Inertia Rails skills.
Once an architectural decision lives in a skill, the AI applies it consistently everywhere, every time. The person behind the wheel still needs to hold the vision but the skill is what makes that vision executable at speed.

The clearest example of this dynamic came from a real bug. Thicket’s app has multiple places in the UI where we display data lists with sorting and filtering capabilities. Originally, all sortable columns in the UI had a one-to-one correspondence with the database table’s columns.
Thus, the pattern AI successfully duplicated from one Rails controller to another looked like this (yes, duplication is totally fine when you’re preparing for the initial launch):
def index
@filters = {states: params.dig(:filters, :states).to_a}
@sort_field = params[:sort_field] || "title"
@sort_direction = params[:sort_direction] == "desc" ? "desc" : "asc"
courses_scope = Current.user.enrolled_courses
courses_scope.order!(@sort_field => @sort_direction, id: :asc)
courses_scope.where!(state: @filters[:states]) if @filters[:states].present?
@pagy, @courses = pagy(courses_scope)
endThe bug emerged when a new sortable field was added on one side (frontend) that wasn’t properly reflected on the other one (backend). More precisely, we needed to implement sorting via an associated (joined) table—something our existing pattern cannot handle.
Just patching the bug wouldn’t fix the problem. A human needed to recognize the pattern and introduce the right abstraction that would prevent similar problems in the future and gate AI generation.
Vladimir Dementyev, Evil Martians’ backend principal and author of Layered Design for Ruby on Rails Applications, added a Filter concept tailored to Thicket needs: a clean abstraction that synced the backend and frontend in a single, type-safe pattern.
The refactored implementation keeps controllers slim and encapsulates explicit filtering schema in dedicated classes:
def index
@courses, @current_filter = filtery(
Current.user.courses.not_removed
)
@pagy, @courses = pagy(@courses)
endAnd the filter object class (we’ll just share a relevant fragment):
class Dashboard::CoursesFilter < ::CoursesFilter
sortable_by :title, :completed_lessons_count, default: "title" do
having "enrolled_at" do |sort_direction: "asc"|
raw.order(enrollments: {purchased_at: sort_direction}).order(id: :asc)
end
end
endAfter that, the AI consistently applied the skill everywhere relevant.
Where we are and what we learned
The process itself changed shape over the four weeks. The early phase involved manual porting and reactive skill-writing as the architecture took form. By the end, Svyat wasn’t writing code by hand. He was orchestrating: reviewing, directing, encoding decisions, and asking AI to fix things. The ratio of effort flipped completely. The last weeks felt like “ready, let’s go.”
Starting fresh runs smoother when you have established patterns and skills from day one. The AI works with you, not against you. You move fast without backtracking.

Svyatoslav Kryukov
Backend Engineer
What we’ve learned going forward is this: from the first Rails commit, the layered Rails skills, the Inertia Rails skills, and the design system skill would all be in place before the first feature was scaffolded. The designer wouldn’t be generating UI from scratch; instead, the designer would be modifying a real, already-configured design system, tweaking what’s there rather than conjuring what isn’t.
This difference shows up everywhere: code consistency, UX coherence, and in how much less the AI has to get right on the first try.
Some final thoughts on agentic coding on Rails:
- It lives on constrained generation; the quality of those constraints determines the quality of the output.
- Rails gives you architectural constraints by default. Skills encode the project-specific decisions on top of them.
- A senior engineer realizes the vision and catches what the AI misses.
When those three things are in place, two people can ship a production platform in weeks not months.


