Your project is deployed, tested, measured and monitored. And yet there is an uncomfortable fact worth stating plainly: nobody knows about it, nobody understands what decisions lie behind it, and you have not yet practiced how to tell that story. Work you cannot show or defend is worth far less than it is — and not out of unfairness, but because whoever is evaluating it has ten minutes and no way of guessing what you did well. This lesson turns what you built into something you can show, defend and improve. You will see how to write a README that makes somebody understand the project in two minutes, with a complete annotated template; how to document architectural decisions with short records (ADRs), and why documenting the why is worth far more than documenting the what; how to prepare a five-minute demo with its script, its narrative order, its prepared data and its plan B; how to talk about the project in a technical interview — including the question you are certain to be asked, "why didn't you use React?" — how to acknowledge a limitation without sounding insecure and how to tell the story of a hard bug; how to assess yourself with a complete rubric across dimensions; how to review your own code and seek external review without being crushed by criticism; how to publish the project with a tidy repository, a clean history and a license; and how to iterate after delivery, which is what separates a finished exercise from a living product.

Contents

  1. Why presenting is part of the work
  2. The README that is understood in two minutes
  3. The complete template, annotated
  4. Screenshots, GIFs and the demo
  5. Documenting decisions: ADRs
  6. The ADR template and five real examples
  7. Why the why is worth more than the what
  8. The five-minute demo
  9. The script and the order of the narrative
  10. Prepared sample data and plan B
  11. Talking about the project in a technical interview
  12. "Why didn't you use React?"
  13. Acknowledging a limitation without sounding insecure
  14. Telling the story of a hard bug
  15. Self-assessment with a rubric across dimensions
  16. Reviewing your own code
  17. Seeking external review and taking criticism
  18. Publishing: repository, history and license
  19. The portfolio
  20. Iterating after delivery
  21. Common Mistakes and Tips
  22. Exercises
  23. Conclusion

  1. Why presenting is part of the work

There is a widespread and false belief: that good work speaks for itself. It does not. What actually happens is this:

Who looks at your project How long they spend What they need to know
A technical recruiter 30–90 seconds What it is, whether it works, whether the repository looks serious
A developer evaluating you 5–15 minutes How it is structured, whether the decisions show judgment
A future colleague 30 minutes Whether they could work on this without asking you
You, a year from now However long it takes Why on earth you did it that way

None of the four is going to read 4,000 lines of code to discover that your cycle detection in the tree covers all three cases. That has to be told.

And there is an argument that goes beyond the portfolio: explaining is a first-order professional skill. In real work you will spend a considerable amount of time justifying decisions, writing documentation, explaining to somebody why something will take longer than it seems, and defending one approach over another. Someone who builds well but cannot explain it has a very concrete professional ceiling, and it is not a technical one.

The good news is that this lesson does not ask you to invent anything: everything there is to tell you have already done. It is a matter of ordering it.

  1. The README that is understood in two minutes

The README.md is your project's front page. It is read more than anything else you have written, including the code.

The two-minute test: give it to somebody who knows nothing about the project and time them. After two minutes they should be able to answer:

  1. What is this and who is it for?
  2. Does it work? Can I see it?
  3. What is technically interesting about it?
  4. How do I run it?

If they cannot, the README fails — however well written it is.

The four mistakes that make it fail the test:

Mistake Why it fails How to fix it
Starting with installation Whoever lands there is not interested in installing anything yet First what it is and a link to the demo
Having no image Nobody pictures an interface from reading A screenshot or GIF in the first 300 pixels
Listing technologies without saying what it does "React, Redux, Tailwind" does not say what the product is What it does first; the decisions afterwards
Hiding the limitations They get discovered anyway and then look like deception Declare them: it is what builds the most credibility

The structure that works, in strict order of decreasing interest:

flowchart TD
    A["Name + one sentence<br/><i>10 seconds</i>"] --> B["Screenshot or GIF<br/><i>20 seconds</i>"]
    B --> C["Link to the demo<br/><i>5 seconds</i>"]
    C --> D["What problem it solves<br/><i>30 seconds</i>"]
    D --> E["What it does · features<br/><i>30 seconds</i>"]
    E --> F["Technical decisions<br/><i>1 minute</i>"]
    F --> G["How to run and test it"]
    G --> H["Architecture"]
    H --> I["Known limitations"]
    I --> J["Roadmap · License"]

    style A fill:#dcfce7,stroke:#16a34a
    style F fill:#dbeafe,stroke:#2563eb
    style I fill:#fef3c7,stroke:#d97706

The three highlighted blocks carry the most weight: the opening sentence decides whether they keep reading, the technical decisions are what set your project apart from ten identical ones, and the limitations are what demonstrate maturity.

  1. The complete template, annotated

<!-- 1 · IDENTITY: name, badges, one sentence. Nothing else. -->
# Orbita

[![CI](https://github.com/user/orbita/actions/workflows/ci.yml/badge.svg)](…)
[![Coverage](https://img.shields.io/badge/domain%20coverage-96%25-brightgreen)](…)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)

**Work manager for small teams**: tasks, subtasks, workload per person and
change history. Built in **plain JavaScript, no frameworks**.

🔗 **[See the demo](https://orbita.example)** · 📖 [Architecture decisions](docs/adr/)

<!-- 2 · THE IMAGE: the first thing anybody looks at. A 10-15 s GIF of the main flow. -->
![Orbita in action](docs/images/demo.gif)

---

<!-- 3 · THE PROBLEM: why it exists. Two or three sentences, no drama. -->
## The problem

A team of three or four people needs to know who is doing what, what is
blocked and who is overloaded. Big tools demand more configuration than they
deliver value at that scale; a spreadsheet falls short as soon as there are
rules to respect (do not close a task with open subtasks, do not exceed
40 hours per person per week).

<!-- 4 · WHAT IT DOES: real features, not adjectives. -->
## What it does

- **Tasks** with status, priority, tags, assignee, reviewer and due date
- **Subtasks** with aggregated hours and progress, and cascading closure rules
- **Multiple filtering** by tags (*all* / *any* mode), assignee and
  status, with the filter reflected in the URL so it can be shared
- **Workload report** per person and ISO week, with an overload warning
- **Immutable history** of every change, with who and when
- **Offline**: changes are queued and synchronized when the network returns
- Installable as a **PWA**

<!-- 5 · TECHNICAL DECISIONS: the section that sets you apart. -->
## Technical decisions

| Decision | Why | Detail |
|---|---|---|
| **No framework** | A single SPA with rich domain logic and a one-person team: the cost of maintaining our own infrastructure (≈ 200 lines) is lower than learning and maintaining somebody else's | [ADR-0002](docs/adr/0002-no-framework.md) |
| **Layered architecture** | The domain must be testable without a browser and survive a change of view. Enforced with ESLint rules that break the build | [ADR-0001](docs/adr/0001-layers.md) |
| **Flat tree with `parentTaskId`** | Lookup and move are O(1); it serializes trivially; the tree is built in memory in a single pass | [ADR-0003](docs/adr/0003-flat-tree.md) |
| **`localStorage` with numbered migrations** | Measured volume: ~900 kB with 500 tasks and 3,000 changes, an order of magnitude below the limit. The repository boundary allows moving to IndexedDB without touching anything else | [ADR-0004](docs/adr/0004-persistence.md) |
| **Optimistic updates with rollback** | The feeling of speed matters more than immediate consistency for reversible operations | [ADR-0006](docs/adr/0006-optimistic.md) |

<!-- 6 · HOW TO RUN IT: commands that work when copied and pasted. -->
## How to run it

git clone https://github.com/user/orbita.git cd orbita npm install npm run dev # http://localhost:5173

Requirements: Node.js 20 or later.

## How to test it

npm test # 251 unit and integration tests npm run test:cov # with a coverage report npm run e2e # 3 end-to-end journeys (Cypress) npm run verify # lint + formatting + tests + build

**251 tests** · **3 E2E journeys** · **96 % domain coverage**

<!-- 7 · ARCHITECTURE: a diagram is worth more than three paragraphs. -->
## Architecture

src/domain/ Entities and rules R1-R15. No dependencies. Tested in Node. src/data/ Persistence and network. One contract, three implementations. src/view/ DOM, events and accessibility. Does not know where data comes from. src/application/ Use cases and state. Joins the three above.

The boundaries between layers are **enforced by ESLint**: an `import` from
`data/` inside `domain/` breaks the build.

<!-- 8 · LIMITATIONS: the section that builds the most credibility. -->
## Known limitations

- **Single device.** With no backend, data does not synchronize between
  browsers. The API layer exists and is tested against a local server,
  but there is no deployed server.
- **No authentication.** The data lives in the browser and **is not private**
  from anybody using the same machine.
- **One level of subtasks** in the interface; the domain supports three.
- **Calendar not optimized** for screen readers with more than 30 tasks
  in a month (see the [accessibility report](docs/accessibility.md)).
- Tested on recent Chrome, Firefox and Safari. No support for older browsers.

## Performance and accessibility

| Metric | Budget | Measured |
|---|---|---|
| Initial JS (compressed) | ≤ 60 kB | 54.1 kB |
| LCP (simulated mobile) | ≤ 2.5 s | 2.1 s |
| CLS | ≤ 0.1 | 0.03 |
| Lighthouse performance | ≥ 90 | 94 |
| Lighthouse accessibility | ≥ 95 | 98 |
| Serious axe violations | 0 | 0 |

Methodology: median of 15 runs, 4× CPU, Slow 4G network, against the
production build. See the [full report](docs/performance.md).

## Roadmap

- [ ] v1.1 · Calendar view and CSV export
- [ ] v1.2 · Three levels of subtasks in the interface
- [ ] v2.0 · Node.js backend with authentication and synchronization

## License

MIT — see [LICENSE](LICENSE).

The badges at the top are not decoration: at a glance they communicate that there is CI, that there are tests and that there is a license. They are one of the few things a technical recruiter interprets in two seconds.

The performance table with its methodology is an uncommon and highly valued detail: it shows you measured methodically, not that you ran Lighthouse once and got lucky.

  1. Screenshots, GIFs and the demo

The image is what gets looked at most and what usually gets the least care.

Format When How to do it well
Static screenshot To show one specific screen Realistic data, no "asdf"; a clean window with no bookmark bars
GIF For the main flow (best for the README) 10–15 s, no sound, looping, < 3 MB
Short video For long flows An external link, not embedded
Live demo Whenever possible With sample data already loaded

The seven rules of the README GIF:

  1. Show the complete main flow, not an isolated click.
  2. Realistic data. No "task 1", "test", "asdf". Use your own fictional but believable data.
  3. Slow, deliberate mouse movement. Fast movements are dizzying.
  4. No toolbars or browser tabs: only the application.
  5. Under 3 MB, or GitHub will be slow to load it and many people will see a blank space.
  6. Start and end in a clean state, so the loop does not jump.
  7. 10–15 seconds. Any longer and nobody finishes it.

The live demo needs prepared data. An empty application demonstrates nothing, and asking a visitor to create six tasks to understand the product is asking too much. Two options:

  • An automatic seed the first time, with a "Start from scratch" button.
  • Demo mode via the URL (?demo=1) loading a sample data set.

And in either case, a clear notice: "Sample data. Everything is stored only in your browser."

  1. Documenting decisions: ADRs

An ADR (Architecture Decision Record) is a short document recording one important decision, its context and its consequences. It comes from a simple observation: the code says what was done, but never why, nor which alternatives were discarded, nor what should be revisited if the context changed.

When to write one. Not for every decision: only for those meeting one of these conditions:

Condition Example in Orbita
It is hard to reverse Layered architecture
It affects the whole project Not using a framework
Reasonable alternatives were discarded Flat tree versus nested
Somebody will ask "why like that?" Deactivating users instead of deleting them
It depends on a context that can change localStorage versus IndexedDB

For a project of this size, between 5 and 10 ADRs is the right number. Fewer than 3 suggests there were no decisions — which is unlikely — and more than 20 suggests you are documenting implementation details.

Where they live: in docs/adr/, numbered, in the repository, versioned with the code. Never in an external service: they drift out of sync.

  1. The ADR template and five real examples

# ADR-0003 · Flat subtask tree with `parentTaskId`

- **Status**: Accepted
- **Date**: 2026-10-08
- **Decided by**: <your name>
- **Related to**: R12, R13, ADR-0004

## Context

Tasks are broken down into subtasks forming a tree up to three levels deep
(R12). We have to decide how that structure is represented in memory and how
it is stored.

Constraints:
- It must serialize to JSON for `localStorage` and for the API.
- Looking up a task by `id` is the most frequent operation (every interface
  event does it through `data-id` delegation).
- Moving a subtask to a different parent must be possible.
- Cycles have to be detected (R12) and depth has to be limited.

## Options considered

### A · Nested: `task.subtasks = [Task, Task]`
- ✅ Reads and renders naturally, recursively.
- ❌ Looking up by `id` requires walking the whole tree: O(n) on every event.
- ❌ Moving a subtask means cutting from one array and pasting into another,
  with two points where the state can end up inconsistent.
- ❌ Serialization nests without limit and complicates migrations.

### B · Flat with `parentTaskId` (chosen)
- ✅ Looking up by `id` is O(1) with a `Map`.
- ✅ Moving is changing **one field**.
- ✅ It serializes as a list; migrations operate on flat elements.
- ❌ The tree has to be built in memory (a ~15-line, O(n) function).
- ❌ Cycles have to be detected explicitly (three distinct cases).

### C · A separate adjacency list (`{ parent: [children] }`)
- ✅ Fast queries in both directions.
- ❌ Two sources of truth that must be kept in sync: exactly the problem the
  architecture is trying to avoid.

## Decision

**Option B.** The cost (building the tree and detecting cycles) is bounded,
is covered by 22 unit tests and is paid once in `tree.js`.
The benefits (O(1) lookup, trivial moves, direct serialization) are collected
on every interaction and every migration.

It is also how a relational database would represent it, which makes the
v2.0 backend easier.

## Consequences

**Positive**
- `buildTree` is O(n) with a `Map`; with 500 tasks it costs < 2 ms.
- Migrations (ADR-0004) operate on a flat list with no recursion.
- Moving a subtask is a single write.

**Negative**
- Integrity has to be validated on load: a `parentTaskId` pointing at a
  non-existent task throws `DataError` instead of silently losing the task.
- Cycle detection requires checking three cases (self-reference, indirect
  cycle, excess depth), each with its own test.

## When to revisit this decision

If the maximum depth were to go above 5 levels or the number of tasks were to
exceed ~50,000, it would be worth re-evaluating with persisted indexes.

The five sections are mandatory and none is superfluous:

Section What it contributes
Context The constraints that existed. Without it, the decision looks arbitrary
Options considered Shows there was an evaluation and not just the first idea
Decision The choice and the argument, not just the choice
Consequences The negative ones too. That is what gives it credibility
When to revisit Turns the decision into something living rather than dogma

The five minimum ADRs for this project:

# Decision Why it deserves an ADR
0001 Layered architecture with enforced boundaries Affects everything; hard to reverse
0002 No framework The question you are certain to be asked
0003 Flat tree There were reasonable alternatives
0004 localStorage with numbered migrations Depends on a measurable context that can change
0005 Deactivating users instead of deleting them Affects the model and the interface; somebody will ask

And two optional ones that look very good if your project has them: optimistic updates with rollback and an append-only immutable history with anonymization instead of deletion.

  1. Why the why is worth more than the what

Compare these two ways of documenting exactly the same thing:

// ❌ Documents the WHAT: the code already says it
// Normalize the name before looking it up in the map
const key = name.normalize('NFC').trim();

// ✅ Documents the WHY: the code cannot say it
// NFC is mandatory: the v1 data mixes precomposed 'í' (U+00ED) with
// 'i' + combining accent (U+0301). They look identical and are not equal.
// Without this line, the v1→v2 migration lost 6 of 8 assignments (see docs/debugging.md).
const key = name.normalize('NFC').trim();

The first comment is noise: it says what you can already read. The second contains information that exists nowhere else and that stops somebody — you — deleting that line six months from now thinking it is redundant.

The general rule:

Document the… Where Example
What In the names. A good name replaces a comment canLink instead of check2
How In the code and in the tests. Tests are the best usage documentation test('rejects an indirect cycle')
Why In comments and ADRs The fragment above
Why NOT In ADRs. It is what nobody documents and is worth the most "Nested was discarded because lookup would be O(n)"

That last row deserves emphasis. Discarded alternatives are the most valuable information and the fastest to be lost. Without them, whoever comes next will propose exactly what you already evaluated and rejected, spend a week finding out, and reach your same conclusion.

  1. The five-minute demo

You are going to have to show your project: in an interview, to a colleague, in a presentation. Five minutes is the typical length, and it has to be prepared and rehearsed.

Why five minutes are hard. Because you know the whole project and you want to tell all of it. The discipline of demoing consists of choosing what not to tell.

The most common mistake, with a name: the guided tour of the interface. "Here is the board, here is the filter button, if I click here this comes up, and here we have another button…". It is boring, it has no narrative tension, and it says nothing about you. Nobody remembers a tour of buttons.

The order that works has the structure of a story:

flowchart LR
    A["1 · PROBLEM<br/>45 s"] --> B["2 · SOLUTION<br/>30 s"]
    B --> C["3 · WALKTHROUGH<br/>2 min"]
    C --> D["4 · TECHNICAL DETAIL<br/>1 min"]
    D --> E["5 · LIMITATIONS<br/>+ roadmap<br/>45 s"]

    style A fill:#fef3c7,stroke:#d97706
    style D fill:#dbeafe,stroke:#2563eb
Step Time What you do Why it works
1 · Problem 45 s You tell the concrete situation, without talking about technology It creates the need. Without it, everything else is a demo of buttons
2 · Solution 30 s One sentence about what it is, and what you chose not to do It frames the scope and heads off the "and why doesn't it do X?" question
3 · Walkthrough 2 min One complete flow, start to finish, with prepared data It proves it genuinely works
4 · Technical detail 1 min One thing you are proud of, told well It is the only thing distinguishing you from ten other demos
5 · Limitations 45 s What it does not do, why, and what comes next It shows judgment and honesty. Almost nobody does it

  1. The script and the order of the narrative

A literal script for Orbita, timed. Adapt it word by word to your project:

## Orbita demo — 5 minutes

### 1 · The problem (45 s)
"A team of three people splits its work over messages and a spreadsheet.
Every week two things happen: somebody ends up with twice the work of the
rest without anybody noticing until it is too late, and big tasks get
called done when in reality half of them is missing.
Big tools solve this, but they demand more configuration than they deliver
value at that scale."

[Do not open anything yet. Let them look at you.]

### 2 · The solution (30 s)
"Orbita is a work manager for small teams. It does three things:
break tasks into subtasks with rules that stop false closures,
show each person's workload per week, and track who changed what.
It is a web application with no framework, it works offline and it installs."

[Open it with data already loaded.]

### 3 · The walkthrough (2 min)
"This is the board of a three-person team. Notice two things:
Iván has 25 open hours and there is an overdue task flagged, with text,
not just with color."

- Create "Prepare the bookbinding workshop", 12 h, assign it to Lucía.
- Break it into two subtasks. The parent's hours become the sum.
- **Try to close the parent with one subtask open** → the warning names
  the subtask blocking it.
  "This is the rule that prevents the 'it's done' that is not."
- Open the workload report: "Lucía goes from 14 to 26 hours this week.
  This used to be discovered on Friday."
- Open the task's history: who, what and when, with no way to edit it.

### 4 · The technical detail (1 min)
"What cost me the most and what I am happiest with is the offline
synchronization."

- Put the browser in offline mode.
- Make three changes. "3 unsynced changes" appears.
- Go back online. They are sent in order and the indicator disappears.

"Every change carries an idempotency key, so if the network drops right
after sending and before receiving the response, resending duplicates
nothing. That case has its own automated test, because by hand it is
impossible to check reliably."

### 5 · Limitations and next step (45 s)
"Three things it does not do, on purpose:
There is no backend, so the data is single-device. The API layer is written
and tested against a local server, but not deployed.
There is no authentication: without a server it would be security theater.
And the calendar was left out of the MVP because the date-sorted list
covered the main need.
Next up is the Node backend reusing the same domain: the fifteen rules are
plain JavaScript with no browser dependencies, so they can run on the
server without duplicating a line."

Five rules for delivering the demo:

  1. Rehearse it out loud at least three times, with a stopwatch. It takes 30 % to 50 % longer than you think.
  2. Do not read the script. Learn the structure, improvise the words.
  3. Do not show code unless asked, and if you bring it up yourself, make it one short, prepared fragment.
  4. Do not apologize for anything. "This is a bit ugly", "I did not have time to…" subtracts without adding. Limitations go in step 5, delivered with confidence.
  5. End with what comes next, not with "and that's it". Leave the feeling of a living project.

  1. Prepared sample data and plan B

The data is half the demo. With "task 1", "task 2" and "test asdf", your product looks like an exercise. With believable data, it looks like a product.

What your demo data set should contain, and why each element:

Element How many What for
People with a name and role 3–4 So the split is understood at a glance
Varied, believable tasks 8–12 Enough to look real, few enough to read
One overloaded person 1 It is the problem the report solves
One overdue task 1 It shows R10 without hunting for it
One task with mixed subtasks 1 It shows R13 during the walkthrough
One inactive user with tasks 1 It shows the ADR-0005 decision if they ask
Coherent tags 5–8 So the multiple filter makes sense

All data fictional, always. Never use real names of colleagues, clients or acquaintances in a public demo or in a repository. It is a matter of respect and, if the project is published, of data protection. Taller Nómada and its team are fictional; yours must be too.

Plan B, which has to be prepared beforehand and not improvised:

What fails Plan B
There is no network The application works offline: use it and make it part of the demo
The deployment is down Have the application running locally, already started, in another tab
The projector changes the resolution Test beforehand; have a comfortable font size
A live failure "Look, that is a bug. I am noting it down." Fix it and carry on. Nobody expects perfection; everybody watches the reaction
Time runs out Have it marked what gets skipped: step 4 gets shortened, step 5 is never cut
Questions midway "Good question, I will answer it at the end so I do not lose the thread"

A technical precaution that has saved many demos: have a 90-second video recording of the main flow. If everything fails — network, laptop, projector — you still have something to show. It takes half an hour to record and it is cheap insurance.

  1. Talking about the project in a technical interview

In an interview, your project is the best tool you have: it is the only thing you know more about than the person interviewing you.

What is actually being assessed when you talk about it is not what most people think:

What you think they assess What they actually assess
How many technologies you used Whether you know why you used each one
Whether it is big Whether it is finished and works
Whether it is original Whether you made decisions and can defend them
Whether it is perfect Whether you know its flaws
How much you know Whether you learned and whether you are someone to work with

The answer structure that works for "tell me about a project of yours", in about 90 seconds:

1. WHAT (15 s)      "It is a work manager for small teams, in
                    JavaScript with no frameworks, deployed and with 251 tests."
2. WHY (15 s)       "I wanted a project with genuine business rules, not a
                    CRUD, to practice architecture and testing."
3. CHALLENGE (30 s) "The most interesting part was the subtask tree: a parent
                    task's hours are the sum of its leaves, and that makes it
                    very easy to double-count. I had a real bug there that
                    cost me an afternoon."
4. DECISION (20 s)  "The decision I am happiest with is having made the domain
                    completely free of browser dependencies. It is tested in
                    Node in milliseconds and I could run it in a backend
                    without duplicating a line."
5. HOOK (10 s)      "If you like, I can show you that part or how it works offline."

Step 5 is what turns a monologue into a conversation: you offer a choice, and the interviewer asks about what interests them. That always goes better than continuing to talk.

The questions you are going to be asked, with the key to each:

Question What they are looking for How to answer
"Why didn't you use X?" Judgment, not dogma Section 12
"What would you do differently?" Self-criticism and learning Something concrete and technical, not "I would do it better"
"What was the hardest part?" How you tackle problems A real problem, with process (section 14)
"How did you test it?" Whether testing is culture or decoration A strategy by levels, not "it has tests"
"How would it scale to 10,000 tasks?" Whether you think beyond your case What you would measure first, not a magic solution
"What is the worst part?" Honesty Something real, and why it is that way (section 13)
"How long did it take you?" Estimation and persistence The truth, broken down by milestone

And the answer to "how would it scale?" deserves a note, because almost everybody answers it badly by improvising optimizations. The good answer starts with measuring:

"First I would measure. My baseline is with 500 tasks: render() at 44 ms and 1,380 nodes. At 10,000, the first thing to break would be the DOM, so I would virtualize the list, which is already prepared because render and update are separated. The second would be localStorage, which falls short at that volume: I would move to IndexedDB, and that only touches one file because the repository is a boundary with a tested contract. The third would be the report, which today recalculates in full; there I would memoize the selector."

That answer demonstrates three things at once: that you measure before optimizing, that you know your own numbers, and that your architecture was designed for it.

  1. "Why didn't you use React?"

You are going to be asked. It is the most likely question of all if your project is plain JavaScript, and it is not a trap: they want to see whether you chose or whether you simply do not know React.

The three worst answers:

Answer What it communicates
"I don't know React" That you did not choose: you were limited
"Frameworks are unnecessary / bloated" Dogmatism, which is the opposite of judgment
"I wanted to learn the basics" Correct but weak: it says nothing about the decision

The good answer has three parts: judgment, honesty and knowledge of the other side.

"It was a decision, and I have the numbers. The product is a single application with a lot of domain logic — fifteen business rules — and little interface surface: seven screens. The team is me. In that context, the infrastructure I need to build myself is about two hundred lines: a store with subscriptions, reconciliation by data-id and event delegation. That is less than the cost of learning and maintaining a framework's conventions for this particular case.

And the figure that convinced me most: in the comparison I ran, the domain rules file was identical in plain JavaScript, React, Vue and Angular. The only thing that changed was the view. Since this project's value is in the domain, the framework added little here.

That said, I know exactly when I would change my mind: with three or more people on the team, or with more than fifteen or twenty screens, those two hundred lines of my own become a problem, because nobody else knows them and there is no documentation or community behind them. There React or Vue clearly pay off. In fact I rewrote the main screen in all four approaches so I could compare: 210 lines and 18 kB in plain JavaScript, 130 lines and 63 kB in React."

Why this answer works:

  1. It starts by asserting it was a decision, not a limitation.
  2. It gives the concrete context that justifies it: product, team, surface area.
  3. It brings a number, not an opinion.
  4. It states when you would change your mind, which is the mark of judgment as opposed to dogma.
  5. It shows you know the alternative with numbers of your own.

And the honest variant if you have not done that comparison: do not invent it. Say what you can defend: "It was a decision based on the context — a product with a lot of domain logic and one person — and I know React well enough to know that with a team of three or with fifteen screens the balance would tip the other way. What I cannot give you are my own numbers from the comparison; I stopped at the reasoning." That is infinitely better than pretending.

The same scheme works for any "why not X?": TypeScript, Tailwind, a particular database. Context → decision → data → when you would change your mind.

  1. Acknowledging a limitation without sounding insecure

There are three ways of talking about a flaw in your project, and only one works:

Approach Example What it conveys
Hiding it Not mentioning it and hoping It gets discovered anyway and looks like deception
Apologizing "It is terrible, I ran out of time, sorry" Insecurity; and it invites people to look there
Framing it "It does not do that, for this reason, and this is what I would do" Judgment and control

The three-part formula, which always works:

1. WHAT is missing or wrong    (direct, no hedging)
2. WHY it is that way          (a conscious decision, or an acknowledged limit)
3. WHAT YOU WOULD DO           (concrete: it shows you know how it is fixed)

Three applied examples:

A limitation by scope decision:

"There is no calendar view. I cut it from the MVP because the date-sorted list covered the main need and the calendar was twelve hours for something secondary. It is on the roadmap as v1.1, and the model already supports it: only the view is missing."

An acknowledged technical limitation:

"Above two thousand tasks the performance would degrade: my baseline is measured with five hundred. I know because I measured, not because I assume it. The solution would be to virtualize the list, and the view already separates render from update precisely so that can be done without rewriting anything."

Something you would do differently:

"I started writing the view before the domain was settled and lost two days redoing work. As soon as I reordered it — domain first, then a vertical slice — the pace changed completely. It is the first thing I would do differently."

All three examples share the same things: they are concrete, they have a number or a reason, and they end in an action. None apologizes, and none hides anything.

A note about the README limitations, because it is a common worry: declaring them does not make your project look worse to whoever is evaluating it. It does the opposite. A project with no declared limitations conveys one of two things: either you have not looked for them, or you are hiding them. Both are worse than having limitations.

  1. Telling the story of a hard bug

It is one of the most frequent questions in technical interviews, and the best opportunity you have to demonstrate how you think instead of what you know.

The SAR format — situation, action, result — structures the answer:

SITUATION (25 %)   The context and the symptom. Concrete.
ACTION (50 %)      How you tackled it. This is where the value is.
RESULT (25 %)      What happened and what you learned.

The classic mistake is spending 80 % on the situation ("it was really weird, there was no way to…") and 20 % on the action. It should be exactly the other way round: the action is what they are assessing.

A complete example, with the race condition bug from 11-04:

Situation. "On a slow network, sometimes — not always — marking a task as done made it come back as pending a second later. On my machine it never happened, and there was no error in the console."

Action. "The first thing was not to touch anything until I could reproduce it at will, because an intermittent failure cannot be debugged. I throttled the network and it happened one time in three, which still was not enough, so I wrapped fetch to delay only PUT requests by three seconds. With that it happened every time.

With the sequence in front of me I saw there were two asynchronous operations: the optimistic PUT, which took three seconds, and a list refresh fired half a second later that arrived first. The refresh replaced the whole list with data the server had generated before my change.

I wrote the failing test first, with fake timers to control the exact order, and only then touched the code. I evaluated three solutions: blocking refreshes while there were in-flight sends — fragile; versioning each task and discarding stale data — the most robust but dependent on the API; and merging while respecting the pending identifiers. I chose the third."

Result. "The bug disappeared and the test stayed in the suite as a regression test. What I learned and have applied ever since is two things: that an intermittent failure is almost always a race between two asynchronous operations, and that any code that replaces a whole state is suspect, because it discards information that may be more recent. I reviewed the rest of the application looking for that pattern and found another place just like it."

The six elements that make this a good answer:

  1. A concrete symptom, with the detail that it was intermittent.
  2. Reproducing first, stated explicitly as a principle.
  3. A concrete technique: delaying only one kind of request.
  4. The test before the fix.
  5. Alternatives evaluated, with the criterion for choosing.
  6. A generalizable lesson, applied afterwards. This is the one that carries the most weight.

Prepare two stories before any interview: one about a hard technical bug and one about a difficult design decision. Write them, time them at two minutes, and rehearse them out loud. It is not cheating: it is not depending on memory under pressure.

  1. Self-assessment with a rubric across dimensions

Before showing the project, assess it yourself. Honestly, because the goal is not the score: it is knowing what you will say when they ask you about the worst part.

15.1 The rubric

Eight dimensions, 0 to 4 points each. Maximum 32.

Dimension 0 · Absent 1 · Initial 2 · Competent 3 · Solid 4 · Outstanding
Functionality It does not work The happy path works Edge cases and errors considered Empty, loading and error states on every screen Plus designed for real use: shortcuts, sample data, export
Domain Logic scattered through the interface Some functions separated A domain layer with rules Complete rules, with invariants and typed errors A dependency-free domain, runnable on a server unchanged
Code quality No conventions Consistent formatting Automatic lint and formatting Clear names, short functions, no duplication Architecture boundaries enforced automatically
Tests None Some unit tests Unit + integration A strategy by levels with reasoned coverage Plus parameterized, documented regressions and a stable suite
Accessibility Not considered Semantic HTML Keyboard and labels Clean axe + full keyboard walkthrough Tested with a screen reader, with a report and limitations
Performance Unmeasured Measured once A defined budget A budget enforced in CI A documented, compared baseline with methodology
Documentation A minimal README A complete README With technical decisions With ADRs and limitations Plus accessibility, performance and debugging reports
Deployment Local only Deployed by hand Continuous deployment With HTTPS, caching and security headers Plus tested rollback, monitoring and PWA updates

15.2 How to read the score

Total Reading What to do
28–32 An excellent portfolio project Present it with confidence; iterate with real feedback
22–27 Solid and defensible Identify the two lowest dimensions and raise them
16–21 Functional with clear gaps Prioritize tests and documentation: the best effort-to-value ratio
10–15 A prototype Finish the MVP before presenting it
< 10 Unfinished Trim the scope until you can close it

The balance rule, which matters more than the total: a 3 across all eight dimensions (24) is better than a 4 in four and a 0 in the other four (16… or even a badly distributed 32). A brilliant project with no tests and no accessibility conveys an unbalanced profile; a project that is solid across the board conveys somebody you can work with.

The mandatory self-assessment question, and the most useful of all:

What is your lowest dimension, and what would you say if they ask about it?

Prepare that answer with the formula from section 13. You will be asked, and having it ready is the difference between looking self-aware and looking caught out.

  1. Reviewing your own code

Before publishing, review your own code as if it were somebody else's. It is uncomfortable and it finds things.

The procedure that makes the shift in perspective possible:

  1. Let time pass. A day at minimum. Your brain fills in gaps in recent code.
  2. Read the project's full diff in GitHub's web interface, not in your editor. The visual context switch works surprisingly well.
  3. Go file by file, from the bottom of the architecture upwards: domain, data, application, view.
  4. Note, do not fix. First the complete list; then the fixes. If you fix while reading, you lose the thread and the perspective.
  5. Classify what you noted into three buckets: fix now, fix later (onto the roadmap), and accept (with the reasoning noted).

The complete checklist:

# Question Warning sign
1 Would a new developer understand the structure in 10 minutes? Folders with no criteria, generic names
2 Is there dead code, commented-out code or an undated TODO? Any commented-out block
3 Are there forgotten console.logs? grep -rn "console.log" src/
4 Do the names tell the truth? A calculate… that also saves
5 Does any function exceed 40 lines? It does more than one thing
6 Is there logic duplicated in three places? An abstraction is missing
7 Are all the business rules in the domain? A business if in an event handler
8 Are errors handled on every path? An await with no try/catch around it
9 Is there an empty catch? It silences failures: the worst pattern there is
10 Do magic numbers have names? A bare 40 instead of MAX_HOURS
11 Is there innerHTML with data? XSS risk
12 Are there secrets, keys or internal URLs? grep before publishing
13 Is there real personal data in the seed or the tests? Names of people you know
14 Do the tests test behavior or implementation? Assertions about internal methods
15 Does everything that connects disconnect? An addEventListener with no unsubscribe
16 Does the README reflect what the project does today? Promised features that are not there

Points 12 and 13 are checked always before publishing a repository, no exceptions.

  1. Seeking external review and taking criticism

Your own review has an insurmountable limit: you cannot see what you do not know exists. That takes other eyes.

Where to ask, with realistic expectations:

Place What to expect How to ask
Somebody you know who codes The best quality, if they have time Be specific: "could you look at the data layer, 20 min?"
Online development communities Variable; sometimes excellent With context, a specific question and a direct link
Forums and code review spaces Aimed at exactly this Follow their rules; be specific
Local groups and meetups Good atmosphere, conversation Show it at an informal meetup
A non-technical person Very underrated For usability: "use it and think out loud"

How to ask for a review that helps. Vague requests get vague answers:

❌ Vague ✅ Specific
"Could you look at my project?" "Structure of a framework-free task manager: does the separation between application/ and view/ look reasonable to you or is it unnecessary ceremony?"
"What do you think?" "Do you see any problem in how I handle synchronization conflicts? It is in data/synchronizer.js, 80 lines."

Always add: what the project is in one sentence, what you have tried, and what specific question you have. And link to a file, not to the whole repository.

How to take criticism, which is the hard part:

Situation Useful reaction
They point out a real bug "Thanks, you are right." Fix it. It is literally free work
They point out something that was a decision Explain the context and listen to the reply. Your context may have been wrong
They say something rudely Separate the content from the tone. The content can be right even if the tone is not
Two people contradict each other Both can be right in different contexts. Ask about each one's context
You do not understand the comment Ask. "Can you give me an example?" is not weakness

And the rule that helps most: criticism of your code is not criticism of you. It is hard to feel that way at first, and it is learned with practice. The mental shortcut that works: the person pointing out a flaw in your code is giving you information you did not have. It is the reaction of gratitude, not of defensiveness, that makes people review your work again.

And not all criticism is accepted. "You should use React" without knowing your context is not actionable criticism. Listen, weigh it up, and if it does not apply, say thanks and move on. Accepting everything you are told is as bad as accepting nothing.

  1. Publishing: repository, history and license

Before making the repository public, a final review:

# Check Command
1 No secrets in the code or in the history git log -p | grep -iE "(api[_-]?key|password|secret|token)"
2 .env ignored and .env.example present git ls-files | grep env
3 No real personal data Review seeds, tests and screenshots
4 No junk files .DS_Store, dist/, coverage/, node_modules/
5 README complete and up to date The two-minute read
6 A license present A LICENSE file
7 Description, topics and demo link on GitHub In the repository settings
8 CI green on main A green badge
9 No dead branches git branch -a
10 No pointless open issues Close or label them

Point 1 has a nuance that surprises a lot of people: deleting a secret in a later commit does not remove it from the history. It is still there, accessible with git log -p. If you ever committed one, the only correct response is to rotate it (change the key in the service); rewriting the history is optional and not sufficient on its own.

A clean history. A readable git log is a sign of professionalism that reviewers look at more than you think:

❌ Careless history ✅ Readable history
changes, fixes, more stuff, asdf feat(domain): detect indirect cycles when linking subtasks
One 3,000-line commit 50–200 line commits, one per increment
WIP, WIP 2, WIP final Every commit leaves the project working

If your history is a mess, you have three honest options: leave it and learn for the next one (perfectly acceptable), clean up only the most recent part, or start a new repository with a careful history if you are at the beginning. What you must not do is rewrite the history of a shared branch.

The license, with what you need to know:

License In one sentence When
MIT Do what you like, credit the author, no warranties The most common and the one recommended for a portfolio
Apache 2.0 Like MIT, plus an explicit patent grant Projects that might be used in a company
GPL v3 Whoever uses and distributes it must publish their code If you want derivatives to stay free
No license Nobody can use it legally Almost never what you want

That last point surprises a lot of people: a public repository with no license is not public domain. By default, full copyright applies, and nobody can legally copy, modify or use it. If you want your project to serve as a portfolio and for somebody to be able to draw inspiration from it, add a license.

And two warnings:

  • Check your dependencies' licenses. Most are MIT or similar and cause no trouble, but it is worth looking (npx license-checker --summary).
  • None of this is legal advice. For a personal portfolio project, MIT is a safe and common choice. For a commercial product, get advice.

  1. The portfolio

Your project has to appear where people will look for it:

Place What to put Length
GitHub, pinned repository Description, topics, demo link 1 line
GitHub profile (README) The featured project with its GIF 3–4 lines
CV Name, one sentence, technologies, links 2 lines
LinkedIn The project with an image and links One paragraph
Personal website, if you have one The complete version One page

On the CV, the format that works:

Orbita — Work manager for small teams                               2026
JavaScript (no frameworks), Vite, Jest, Testing Library, Cypress, PWA
Application with a layered architecture and 15 business rules in a domain with
no browser dependencies. 251 tests (96 % domain coverage), CI with performance
and accessibility budgets, continuous deployment and offline operation with an
idempotent synchronization queue.
Demo: orbita.example · Code: github.com/user/orbita

What makes that block good: there are concrete numbers (15 rules, 251 tests, 96 %), there are concepts that demonstrate level (layered architecture, budgets in CI, idempotency), and there are links. There are no empty adjectives: no "robust", no "scalable", no "modern".

One well-told project is worth more than three half-done ones. If you have several, pick the best, tell it fully, and mention the others in one line.

  1. Iterating after delivery

Here is the difference between an exercise and a product: an exercise is delivered and it ends; a product has a next version.

How to get real feedback, which is the most valuable thing and the least often done:

Source How What you get
Watching somebody use it Give them a specific task, sit next to them and do not help The most valuable thing by far
Thinking out loud "Say what you are thinking while you use it" Where they hesitate and why
A short form 3 questions maximum Little depth, some volume
Analytics What gets used and what does not Data without the why
GitHub issues A visible link in the README From technical people

The technique of watching somebody use it deserves detail because it is brutally effective and almost nobody applies it: you give them a task ("create a task with two subtasks and find out who is most loaded this week"), you keep quiet, and you observe. The rule is never to help. Every time you feel the urge to say "you have to click there", that is a design flaw you have just found. With three people you discover 80 % of the usability problems.

How to prioritize what comes in. Not everything gets done, and deciding is the skill:

High impact Low impact
Low effort Do it now Do it if there is time to spare
High effort Plan it properly Discard it

With a preliminary filter of three questions for each request:

  1. How many people have asked for it? One passionate request from one person is not a trend.
  2. Does it fit the product sentence (11-01)? If not, it is probably a different product.
  3. What breaks if I do it? Every new feature adds surface area to maintain.

Planning the next version is repeating the 11-01 cycle in miniature: pick three or four things, write them as stories with criteria, estimate with your correction factor now calibrated by the previous project — which is now real and not an assumption — and set yourself a date.

And a note on when to stop. Not every project has to continue forever. A project can be finished in the sense that it delivers what it promised. If you decide not to continue, say so in the README:

## Project status

Orbita is **finished** as a learning project: it meets its MVP, it is
deployed and documented. It is not under active development, but issues
are accepted and security bugs are fixed.

That is far more honest than a repository whose last activity was two years ago with a roadmap full of unticked boxes.

Common Mistakes and Tips

A README that starts with installation. Whoever lands there is not interested in installing anything until they know what it is and what it is for. Name, sentence, image, demo, problem — and then the rest.

Not including any image. It is the most expensive README failure, because 90 % of the people who open it look for an image before reading a word. A twelve-second GIF is worth more than three paragraphs.

Listing technologies instead of saying what the product does. "React, Redux, Tailwind, Vite" does not say whether it is a task manager or a game. Technologies go in the decisions section, with their reasoning.

Hiding the limitations. They get discovered anyway, and then it looks like you were hiding them. Declaring them builds the most credibility, and it is what almost nobody does.

Documenting the what instead of the why. A comment repeating what the code says is noise that also goes out of date. The why, the discarded alternatives and the context are the only things the code cannot tell you.

A demo that is a tour of buttons. With no problem at the start, there is no narrative tension and nobody remembers anything. Problem, solution, one complete flow, one technical detail, limitations.

Demoing with test data. "Task 1", "asdf", "test test" make your product look like a classroom exercise. Prepare a believable, fictional data set.

Not rehearsing the demo. It takes 30–50 % longer than you think, and without rehearsal you hit minute five halfway through. Three run-throughs out loud with a stopwatch.

Answering "I don't know React" to "why didn't you use React?". It turns a decision into a limitation. Context, decision, data, and when you would change your mind.

Apologizing for your project. "It is a bit ugly", "I ran out of time". It subtracts, adds nothing, and invites people to look exactly where you do not want them to. Limitations are framed, not apologized for.

Publishing without reviewing the history. A secret committed three months ago is still accessible with git log -p. If it happened, the answer is to rotate the key, not just delete it.

Publishing with no license. A public repository with no license cannot be legally used by anybody. If it is a portfolio, MIT and be done.

Tip · Write the README as if it were for somebody in a hurry with no context. Because it is. Short sentences, tables, lists, and the important things at the top.

Tip · Keep the GIF and screenshots in the repository, in docs/images/. External image services expire and leave holes in your README two years from now.

Tip · Write ADRs the same day you make the decision. Reconstructing the reasoning a month later is impossible: you will remember the conclusion, not the alternatives or the why.

Tip · Rehearse the demo by recording yourself. It is uncomfortable to watch yourself, and it is the fastest way to spot filler words, rushing and the parts where you lose your way.

Tip · Have two stories prepared, a hard bug and a design decision, written and timed at two minutes. You will be asked for them, and you will not want to improvise.

Exercises

These exercises close milestone H6 of your project: README, ADRs, demo script and a completed self-assessment.

Exercise 1 — The README and the ADRs.

  1. Write the complete README.md with the template from section 3, adapted to your project: identity with badges, image, demo, problem, features, a technical decisions table with links to ADRs, how to run it, tests with real numbers, architecture, known limitations, a performance table with methodology, roadmap and license.
  2. Record a 10–15 second GIF of the main flow meeting the seven rules from section 4, stored in the repository, under 3 MB.
  3. Prepare the demo data with the seven elements from the table in section 10, all fictional, and add demo mode or the automatic seed.
  4. Write at least five ADRs with the complete template from section 6, with their five sections — including the negative consequences and the "when to revisit" — and with at least two evaluated alternatives in each.
  5. Run the two-minute test: give the README to somebody who does not know the project, time them, and ask them to answer the four questions. Note what they could not answer and fix the README.

Exercise 2 — The demo and the interview answers.

  1. Write the five-minute script with the five-step structure, with the timings noted and what you will say at each step.
  2. Rehearse it three times out loud with a stopwatch and adjust it until it fits in five minutes with room to spare.
  3. Record yourself doing it and review it. Note three things to improve.
  4. Prepare the complete plan B: the table of six situations adapted to your context, plus a 90-second recording of the main flow as a last resort.
  5. Write and rehearse the answers to these six questions, each in under two minutes:
    • "Tell me about this project" (the five-step structure from section 11).
    • "Why didn't you use React?" (or whichever framework applies), with context, decision, data and when you would change your mind.
    • "What is the worst part of your project?" (the three-part formula from section 13).
    • "Tell me about a hard bug" (SAR format, with a generalizable lesson).
    • "How would it scale to 10,000 items?" (starting with measuring).
    • "What would you do differently?" (concrete and technical).

Exercise 3 — Self-assessment, review and publication.

  1. Complete the rubric of eight dimensions from section 15, with a one-sentence justification for each score. No scoring from memory: open the project and check.
  2. Identify your two lowest dimensions and write a concrete plan for raising them, with the estimated effort.
  3. Prepare the answer to "what is your weakest point?" with the three-part formula.
  4. Do the self code review with the 16 checks from section 16, after letting at least a day pass. Document the findings classified into the three buckets: fix now, fix later, and accept with the reasoning.
  5. Ask for an external review with a specific question about a specific part. Document what you were told, what you accepted and what you discarded with the reason.
  6. Get at least one person to use your application while you watch, without helping. Note every moment of hesitation: each one is a design flaw.
  7. Run the 10 pre-publication checks from section 18, including the Git history one. Add a license, description, topics and demo link.
  8. Add it to your portfolio: pinned repository, profile README, and the CV block with concrete numbers.
  9. Plan the next version: three or four improvements prioritized with the impact/effort matrix, written as stories with criteria, and with a date.

Solutions

Rubric for exercise 1 — README and ADRs (24 points)

Dimension 0 1 2 3
The two-minute test It fails With help It passes It passes and the reader wants to try it
Image None Screenshot GIF of the flow GIF with believable data and all 7 rules
Problem Absent Mentioned Concrete With the real situation motivating the product
Technical decisions None A list of technologies A table with reasons With links to ADRs and data backing them
Limitations Hidden One or two A complete list With a reason and a plan for each
Instructions Incomplete They work With requirements Copied and pasted they work first time
ADRs: number and form < 3 3–4 5 complete 5+ with all five sections
ADRs: quality The decision only With context With alternatives With negative consequences and "when to revisit"

Threshold: 17/24, with a mandatory ≥ 2 in "The two-minute test" and in "ADRs: quality".

Acceptance criteria for exercise 1

# Criterion Verification
1 An outsider answers the 4 questions in 2 min A real, timed test
2 The GIF is under 3 MB and lasts 10–15 s File properties
3 The demo data is fictional and believable Review
4 The seven demo-set elements are there Checked list
5 Each ADR has ≥ 2 evaluated alternatives Reading
6 Each ADR states negative consequences Reading
7 The instructions work on a clean machine Clone into another folder and run
8 The README numbers are real Compare with npm run test:cov

Acceptance criteria for exercise 2 — Demo

# Criterion Verification
1 The demo lasts under 5 min A stopwatch on the recorded rehearsal
2 It starts with the problem, not the interface The first 45 s with nothing open
3 One complete flow, not a tour of buttons The script
4 One technical detail told in depth The script
5 The limitations are stated without apologizing The recording
6 There is a plan B for all six situations The document
7 The 90-second backup recording exists The file
8 The six answers last < 2 min each Timed
9 The framework answer includes a number of your own Content
10 The bug story devotes ≥ 50 % to the action Text analysis
11 The bug story ends in an applied lesson Content
12 No answer contains "I don't know" with nothing after it Review

Rubric for exercise 3 — Review and publication (21 points)

Dimension 0 1 2 3
Honesty of the self-assessment Inflated Approximate Justified With evidence per dimension
Improvement plan None Generic Concrete With estimated effort and prioritized
Self review Not done Superficial All 16 checks With findings classified into three buckets
External review Not requested Requested vaguely A specific question Documented with what was accepted and what was not, and why
User test Not done You asked for an opinion You observed without helping With a list of hesitation moments and fixes
Publication Unreviewed Basic checks All 10 Including the history and with a reasoned license
Portfolio It does not appear A loose link Pinned repository and CV With concrete numbers and no empty adjectives

Threshold: 14/21. With one condition that cannot be traded away: zero secrets and zero real personal data, not in the code, not in the history and not in the screenshots.

The project's final self-assessment. Before considering the milestone closed:

Question Yes / No
Does somebody who does not know my project understand it in two minutes?
Can I explain every important technical decision with its discarded alternative?
Have I rehearsed the demo out loud with a stopwatch?
Do I know what to answer to "why didn't you use a framework?" with a number of my own?
Do I know my weakest point and what I will say when asked about it?
Have I watched somebody use my application without helping them?
Is my repository clean, licensed and free of secrets in the history?
Do I have the next version written down, or have I declared it finished?

Conclusion

You have turned a project that worked into a project that can be shown, defended and improved — and that difference is worth as much as the code.

You know why presenting is part of the work: because nobody is going to read 4,000 lines to discover what you did well, because whoever is evaluating you has between 90 seconds and 15 minutes, and because explaining decisions is a first-order professional skill that anybody who builds well but cannot tell the story runs into.

You have a README that passes the two-minute test, with the decreasing-interest order that works: identity, image, demo, problem, features, technical decisions — the section that sets you apart from ten identical projects — and known limitations, the section that builds the most credibility and that almost nobody writes. With badges that communicate in two seconds, with the performance table accompanied by its methodology, and with instructions that work copied and pasted. And with a GIF that meets the seven rules, because the image is the first thing people look at and usually gets the least care.

You have ADRs for the five decisions that deserve them, with the five mandatory sections — context, evaluated alternatives, decision, consequences including the negative ones, and when to revisit — and you know why the why is worth more than the what: the code says what was done, the names and tests say how, but only an ADR says why not it was done the other way. That is the most valuable information and the fastest to be lost, and without it whoever comes next will spend a week reaching your same conclusion.

You have a five-minute demo with the structure of a story instead of a tour of buttons: problem, solution, one complete flow, one technical detail told well, and limitations with a roadmap. With the five delivery rules — rehearse three times with a stopwatch, do not read, do not show code unless asked, do not apologize for anything, and end with what comes next — with fictional, believable data containing the seven elements that make the product explain itself, and with a plan B for six situations plus a 90-second recording that is the cheapest insurance there is.

You know how to talk about the project in an interview understanding what is genuinely assessed: not how many technologies you used but why; not whether it is big but whether it is finished; not whether it is perfect but whether you know its flaws. You have the five-step structure ending in a hook — "if you like, I can show you…" — that turns a monologue into a conversation. And you have prepared the question you are certain to be asked, "why didn't you use React?", with the three worst answers identified and a good one built on context, decision, a number of your own and — what separates judgment from dogma — when you would change your mind. With the honest variant for when you do not have the number, because inventing it always turns out worse.

You know how to acknowledge a limitation without sounding insecure with the three-part formula — what is missing, why it is that way, what you would do — which turns a flaw into a demonstration of judgment. And you know how to tell the story of a hard bug in SAR format, devoting half to the action and not to how strange it was, with the six elements that make it good and, above all, with a generalizable lesson you applied afterwards.

You have assessed yourself with a rubric of eight dimensions and 32 points, knowing that balance matters more than the total — a 3 across all eight is worth more than a 4 in four and a 0 in the rest — and with the mandatory question answered: which is your lowest dimension and what you will say when asked about it.

You know how to review your own code with distance, in the web interface, from the bottom of the architecture upwards, noting before fixing and classifying into three buckets. And you know how to seek external review with specific rather than vague requests, and to take criticism with the rule that changes everything: whoever points out a flaw in your code is giving you information you did not have. With the nuance that not all criticism is accepted, and that accepting everything is as bad as accepting nothing.

You know how to publish with the ten pre-publication checks, including the surprising one — that deleting a secret does not remove it from the history, and that the only correct answer is to rotate the key — with a readable history, and with a license, because a public repository with no license cannot be legally used by anybody. And you know how to put it in your portfolio with concrete numbers and no empty adjectives.

And you know how to iterate after delivery, which is the difference between an exercise and a product: watching somebody use it without ever helping — every urge to help is a design flaw found — prioritizing with the impact/effort matrix and the three filter questions, and planning the next version by repeating the 11-01 cycle with an estimation factor that is no longer an assumption but a fact from your own project. Or honestly declaring that it is finished, which is also a valid answer and far better than an abandoned roadmap.

Milestone H6 is closed, and with it the project: planned, built, persisted, tested, deployed, documented and defensible. One lesson remains, and it is not about the project: it is the balance sheet of everything you now know, the map of what comes next — TypeScript, Node.js, a framework in depth, the web platform, your career — and how you keep learning when there is no longer a course telling you what comes next. It is Next Steps: TypeScript, Node.js and Your Career.

JavaScript Course: From Beginner to Advanced

Module 1: Introduction to JavaScript

Module 2: Control Structures

Module 3: Functions

Module 4: Objects and Arrays

Module 5: Advanced Objects and Functions

Module 6: The Document Object Model (DOM)

Module 7: Browser APIs and Advanced Topics

Module 8: Testing and Debugging

Module 9: Performance and Optimization

Module 10: JavaScript Frameworks and Libraries

Module 11: Final Project

© Copyright 2026. All rights reserved