Cross-cutting notes, opinions, and gotchas that don't fit in a single folder. Skim once, return as needed.
- Parse URL β browser splits scheme, host, path, query.
- DNS lookup β host β IP (cached at browser, OS, router, ISP).
- Open TCP socket β 3-way handshake to IP:443.
- TLS handshake β negotiate cipher, verify cert, exchange keys.
- HTTP request β
GET /path HTTP/1.1+ headers. - Server processes β routes, middleware, handler, DB.
- HTTP response β status + headers + body.
- Browser parses HTML β incremental render.
- Subresource fetches β CSS, JS, images (in parallel, with priorities).
- JS executes β DOM ready, then load event.
- Paint β pixels on screen.
Total time = TTFB + render. β Performance/
Every millisecond you shave off DNS/TCP/TLS/TTFB compounds. Use dns-prefetch, preconnect, HTTP/2 or HTTP/3, edge CDNs, and keepalives.
The browser is a remarkably capable platform. Before reaching for a library, check if the platform does it:
| Need | Native API |
|---|---|
| Intersection-based lazy load | IntersectionObserver |
| Resize handling | ResizeObserver |
| Mutation watching | MutationObserver |
| Smooth scroll | scrollIntoView({ behavior: 'smooth' }) |
| Sticky positioning | position: sticky |
| Dark mode | prefers-color-scheme |
| Reduced motion | prefers-reduced-motion |
| Forms validation | Constraint validation API |
| Modal | <dialog> |
| Accordion | <details> |
| Progress | <progress>, <meter> |
| Custom select | <select> with custom parts (limited) β otherwise Combobox pattern |
| Drag and drop | HTML5 DnD API (or Pointer Events for custom) |
| Clipboard | navigator.clipboard |
| Geolocation | navigator.geolocation |
| Notifications | Notification API |
| Service worker / offline | Service Worker API |
| Web sockets | WebSocket |
| Server-sent events | EventSource |
| Web workers | Worker, SharedWorker |
| Animations | Web Animations API |
| Local DB | IndexedDB (or localStorage for tiny stuff) |
| File system | File System Access API |
| Share | navigator.share |
| Payments | Payment Request API |
| Credentials | Credential Management API, WebAuthn |
When the platform doesn't do it well, reach for a library. β EXTERNAL_REPOSITORIES.md
- We estimate the happy path.
- We forget integration, testing, deploy, bug-fixing.
- We're optimistic to please.
- Unknowns are unknown.
- Requirements change.
- Three-point: optimistic, likely, pessimistic. Report the range, not a single number.
- Decompose: break into <1-day chunks. Sum + 30% buffer.
- Historical: "this looks like feature X, which took 5 days."
- T-shirt size first (S/M/L), then days, then hours.
- Update as you learn. Re-estimate weekly.
- "It'll take 2 weeks" β team commits to a date based on that, with no buffer.
- "I'll do it this weekend" β it won't be this weekend.
- "Almost done" for 3 weeks.
Software takes 2β3x longer than your first guess, on average. Plan accordingly.
You'll spend more time reading than writing. Get good at it.
- Read the README.
- Read the entry point (e.g.,
index.ts,app/page.tsx). - Follow one request through the stack end-to-end.
- Read the data model.
- Read the tests β they document intent.
- Skim the rest as needed.
- Don't read linearly.
- Use go-to-definition aggressively.
- Use find-all-references to map call sites.
- Draw a diagram as you go.
- Note questions; answer them by reading, not by asking.
- Review the test, not just the code.
- Question naming, structure, abstractions.
- Verify the bug actually exists (repro).
- Don't nitpick style (the linter should).
- Suggest, don't dictate.
- Praise good work.
- Don't block on minor stuff. Comment with "nit:".
- Ask "why?" when something is unclear.
- Self-review before requesting.
- Write a clear PR description (problem, solution, why this approach, how to test).
- Keep PRs small.
- Respond to every comment (even with "agree, will fix in follow-up").
- Don't take feedback personally.
- "This is wrong" with no explanation.
- Bikeshedding on naming when the structure is the issue.
- Asking for changes the linter could enforce.
- Requesting a feature in a bug-fix PR.
Set them early. Enforce in CI.
| Resource | Mobile budget | Desktop budget |
|---|---|---|
| HTML | 50 KB | 100 KB |
| CSS | 50 KB | 100 KB |
| JS (gzip) | 150 KB | 300 KB |
| Fonts | 100 KB | 200 KB |
| Images | 500 KB | 1 MB |
| Total | < 1 MB | < 2 MB |
| LCP | < 2.5s | < 2s |
| INP | < 200ms | < 200ms |
| CLS | < 0.1 | < 0.1 |
For the 80% case, choose:
- The thing your team knows.
- The thing with 5+ years of production use.
- The thing with docs, community, and StackOverflow answers.
Save the novel tech for the 20% where it actually matters.
This is why Postgres + Node + React + Next.js is the boring default. It's not exciting, but it ships.
Almost never. Rewrite signals:
- The current system can't be incrementally fixed.
- The cost of adding features exceeds the cost of starting over.
- The team has the appetite and the runway.
Even then:
- Strangler fig pattern: replace piece by piece.
- Keep the old system running until the new one is proven.
- Don't rewrite + redesign + change team all at once.
The classic mistake: "Let's rewrite in !" 18 months later, neither version ships.
Complexity has two sources:
- Essential β inherent to the problem.
- Accidental β introduced by the tools/approach.
You can't reduce essential complexity. You can avoid accidental complexity. Most "hard" software is hard because of accidental complexity.
When something feels hard, ask:
- Is this problem genuinely hard?
- Or did I pick tools/approaches that made it hard?
The latter is fixable. β Architecture/, ANTI-PATTERNS.md
Not all debt is bad. Borrowing ships features. But:
- Track it. TODO comments, issues, a "debt" board.
- Pay it down regularly. 20% of sprint capacity, minimum.
- Don't refactor "because it's ugly". Refactor when changing the code is harder than it should be.
- Don't refactor without tests. You'll break things.
If a junior engineer asks "why is it like this?" and the answer is "we've always done it that way" β that's a smell. Either the design has a reason (document it) or it's debt (pay it).
The most effective engineering teams default to async.
- Write decisions down.
- Don't interrupt deep work.
- Meetings are for decisions that need real-time back-and-forth, not status.
- Use PRs / docs / issues as the primary medium.
- Sync meetings produce notes / decisions, not action.
- Author writes RFC (problem, options, recommendation).
- Comments for N days.
- Decision meeting (or async decision).
- Decision recorded in ADR. β Architecture/adrs.md
- Don't do leetcode hazing for senior roles.
- Do pair on a real problem.
- Look for: clarity, curiosity, ownership, kindness.
- Test for the actual job, not trivia.
- Have a portfolio. Ship things.
- Read the job description. Tailor your story.
- Ask questions about the team, the codebase, the oncall.
- Be honest about what you don't know.
- Single-task. Multitasking is a myth.
- Block deep work time. Defend it.
- Turn off notifications during deep work.
- End the day with a clear "next action" written down.
- Friday afternoon: review the week, plan the next.
- Keep a "today I learned" log.
- Specialize early, generalize later. (Or vice versa, but know which you're doing.)
- Write in public. It compounds.
- Teach what you learn. You'll learn it better.
- Find mentors. Be a mentor.
- The salary jump is in the job change. The skill jump is in the side project.
- Don't optimize for the title. Optimize for the work.
- Tailwind is the right default for new projects. β CSS/
- shadcn/ui is the right component default. β EXTERNAL_REPOSITORIES.md
- Postgres is the right default database for 95% of new apps. β PostgreSQL/
- Prisma or Drizzle; both fine; pick by team preference. β ORM/
- Next.js App Router for the default React meta-framework. β Templates/_next-app/
- Vite for SPAs that don't need SSR.
- Vitest over Jest for new projects.
- Playwright over Cypress for new projects.
- Zod for runtime validation.
- Hono for edge-first APIs.
- Bun is fast but Node 20 is still the safe default for production.
- Vercel/Cloudflare for frontend deploys. AWS/GCP for backend heavy lifting.
- Linear for issue tracking. (Opinionated, but most agree.)
These are defaults, not commandments. Deviate when you have a real reason.
The actual 10x engineer:
- Writes less code (deletes more).
- Asks the right questions before writing.
- Catches the bug in design, not in QA.
- Makes teammates better.
- Documents as they go.
- Says "no" to scope creep.
- Doesn't work 80-hour weeks (they'd be slower, not faster).
The fake 10x engineer:
- Writes 10x the code (most of it bad).
- Doesn't review PRs.
- Hoards knowledge.
- Burns out teammates.
Be the first kind.
Next: RESOURCES.md Β· BOOKS.md Β· COMMUNITIES.md