AI is stealing your time.

Your spec is still a prompt.

Every run, AI interprets it again — a different guess each time. Source shouldn't be prompt-guessed. Make it compiled.

interpreted every run

Subscriptions can be cancelled at any time.
Cancelled subscriptions stay active until the end of the current billing period.

Subscription
============
** id          : ID
*  owner       : Reference@Customer
*  status      : String
-  cancelledAt : Date

Cancellation Rules
??????????????????
@ input:: subscription: Subscription
@ output:: canCancel: Boolean

rule activeOnly
| when: subscription.status == "ACTIVE"
| then: canCancel = true
controller.ts service.ts entity.ts dto/ module.ts requests.http
The problem

Your app's behavior is hidden in your code.

One rule. Five files. No single place to read it.

And AI makes it worse. Code got cheaper. Your attention didn’t.

When can a subscription be cancelled?

  • guard on the route
  • status must be ACTIVE
  • optional cancelledAt
  • access until period end — in a test
  • mid-cycle cancel flag
No single place to read it.

subscription.controller.ts

import { Controller, Post } from '@nestjs/common';

@UseGuards(ActiveSubscriptionGuard)

@Post(':id/cancel')

subscription.service.ts

async cancel(id: string) {

if (sub.status !== 'ACTIVE') throw new ConflictException()

await this.repo.save(sub)

subscription.schema.ts

export const Subscription = z.object({

cancelledAt?: Date

})

cancel.e2e-spec.ts

describe('cancel subscription', () => {

it('keeps access until period end', …)

expect(res.status).toBe(200)

billing.config.ts

export default {

allowMidCycleCancel: false

prorate: true

The answer

Source Code has a missing counterpart.

Meet the Source Model.

Code says HOW. The Source Model says WHAT.

Typed. Versioned. Validated. Compiled. Kept for the next change.

Today: structure, relationships, operations. Next: outcomes and constraints.

WHY
Intent
The purpose.
WHAT
Source Model
The domain, relationships, and behavior.
HOW
Source Code
The implementation.
subscription.modelhike — the rule, in one place
Subscription
============
** id          : ID
*  owner       : Reference@Customer
*  status      : String
-  cancelledAt : Date

Cancellation Rules
??????????????????
@ input:: subscription: Subscription
@ output:: canCancel: Boolean

rule activeOnly
| when: subscription.status == "ACTIVE"
| then: canCancel = true

rule notActive
| when: otherwise
| then: canCancel = false

One rule. One place. Readable and compilable.

Why not specs

Spec-driven dev has the right intent. And the wrong implementation.

It made the spec the source of truth. Then asked AI to guess the code.

  1. 01
    The spec is prose. Prose is ambiguous.

    Natural language leaves the HOW open to interpretation. If implementation details are still debatable, the foundation is already weak.

  2. 02
    The AI is non-deterministic.

    Same spec, same model, two runs: two codebases, two different bugs.

  3. 03
    The human is the bottleneck.

    Every generated diff, reviewed line by line, by an exhausted senior at 4:47pm on a Friday.

Prose in. LLM guess out. Different every time.

Three flaws, compounding.

The contrast

Spec-driven vs Model-Native.

Same goal. A better way for the AI-era.

Spec AI interprets Code a91f…c2
  • Interpreted.
  • Regenerated.
  • Re-reviewed.
Source Model Compiler + Blueprints Code b7d3…e4
  • Validated.
  • Compiled.
  • Reviewed once.

Stop treating the spec as instructions for AI. Start treating it as source.

The idea

Welcome to Model-Native Development.

ModelHike fixes all three — by moving up from source code to the Source Model.

  • fixes: ambiguity Declaration, not prose.

    Stop describing your app in English and hoping AI reads it right. Define entities, APIs, relationships, and rules precisely in one typed DSL.

  • fixes: drift Compilation, not generation.

    Same model + blueprint → same output. Auditable. Reproducible. Shippable.

  • fixes: the bottleneck Review at the source.

    Review a few lines of model change, not hundreds of generated files.

  • keeps AI where it helps AI in the loop.

    AI in your editor drafts and refines the model through MCP. The compiler handles the rest: grounded, repeatable, reviewable.

+1 rule
subscription.modelhike
Subscription
============
** id : ID
*  owner : Reference@Customer
-  cancelledAt : Date

#blueprint(api-springboot-monorepo)

Cancellation Rules
rule activeOnly
rule notActive

planning.md

Cancellation policy — please review…

Not a planning doc.

discarded

chat · prompt

“Generate a subscription service that…”

Not a discarded prompt.

discarded

output

NestJS · 37 files

generated

architecture.png

▦ service → repo → db

Not a stale diagram.

stale

output

Spring Boot · Java

generated

A model that outlives your code.

  • Model what's meaningful.
  • Code what's necessary.
  • Automate what's repeatable.
No code cage.

Output is real source code you can read, test, and change.

Meet ModelHike

Own the WHAT. Automate the HOW.

ModelHike is the markdown-like language for application modelling.
You write the Source Model. Blueprints compile it into real, readable code.

38 lines in. 1,418 lines out.

app.modelhike · the model
// Built for AI to write, designed for Humans to read.
====
APIs #blueprint(api-springboot-monorepo)
====
+ Flight Module

=== Flight Module ====

Flight
==============
** id : ID
* arrival Station : Reference@Station
- aslot : String
- arrival Status : String
* flight Key : String

getPilotLevel(empId: Int) : String
─────────────────────────────
|> DB Employee · WHERE · FIRST
|> IF years < 5 → "Mid-Level"
|> ELSE → "Senior"
─────────────────────────────

# APIs ["/flight"]
@ apis:: create, delete, list, get-by-id

Station
==============
** id : ID
* code : String

Recorded output · a flight service · api-springboot-monorepo

37files generated
1,418lines of output
20Java files
.dockerignore.gitignoreDockerfileREADME.mddocker-compose-apps.ymldocker-compose-base-services.ymlgradlewgradlew.batsettings.gradlebuild.gradlesettings.gradleapplication.ymlflightmodule.graphqlsApp.javaAppConfig.javaApis.httpFlightController.javaCreateFlightCommand.javaDeleteFlightCommand.javaGetFlightByIdQuery.javaListFlightsQuery.javalistFlightsByArrivalStationQuery.javaFlight.javaFlightInput.javaFlightRepository.javaApis.httpStationController.javaCreateStationCommand.javaDeleteStationCommand.javaGetStationByIdQuery.javaListStationsQuery.javaUpdateStationCommand.javaStation.javaStationInput.javaStationRepository.javaflightmodule.pumlgradle-wrapper.properties

Recorded output · 37 files · 1,418 lines.

Portability

The model stays. The technology can change.

Same model. Different blueprint.

NestJS
  • TypeScript
  • REST
  • CQRS
  • MongoDB
  • libs/domain-models/domain.entities.ts
  • flight/crud/create.flight.ts
  • flight/controller.ts
  • flight/module.ts
  • flight/requests.http

Template-derived structure — not a recorded generation.

app.modelhike — the model
====
APIs #blueprint(api-springboot-monorepo)
====
+ Flight Module
=== Flight Module ====
Flight
==============
** id : ID
* arrival Station : Reference@Station
- aslot : String
Spring Boot 3
  • Java
  • GraphQL
  • WebFlux
  • MongoDB
  • flight/FlightController.java 63
  • flight/model/Flight.java 39
  • flight/crud/ ×5 commands & queries
  • flight/Apis.http 104
  • resources/graphql/flightmodule.graphqls

Recorded output · real generated files.

Own your business logic. Not just your codebase.

NestJS and Spring Boot today. More coming soon…

Your move

Your spec was a prompt.
Make it a Source Model.

Clone it. Build it. Validate your first model.

git clone https://github.com/modelhike/modelhike
Explore on GitHub
  • Open source.
  • Real code.
  • No code cage.