Remix.run Logo
EGreg 6 days ago

Programmers maybe

But software architects (especially of various reusable frameworks) have to maintain the right set of abstractions and make sure the system is correct and fast, easy to debug, that developers fall into the pit of success etc.

Here are just a few major ones, each of which would be a chapter in a book I would write about software engineering:

ENVIRONMENTS & WORKFLOWS Environment Setup Set up a local IDE with a full clone of the app (frontend, backend, DB). Use .env or similar to manage config/secrets; never commit them. Debuggers and breakpoints are more scalable than console.log. Prefer conditional or version-controlled breakpoints in feature branches. Test & Deployment Environments Maintain at least 3 environments: Local (dev), Staging (integration test), Live (production). Make state cloning easy (e.g., DB snapshots or test fixtures). Use feature flags to isolate experimental code from production.

BUGS & REGRESSIONS Bug Hygiene Version control everything except secrets. Use linting and commit hooks to enforce code quality. A bug isn’t fixed unless it’s reliably reproducible. Encourage bug reporters to reset to clean state and provide clear steps. Fix in Context Keep branches showing the bug, even if it vanishes upstream. Always fix bugs in the original context to avoid masking root causes.

EFFICIENCY & SCALE Lazy & On-Demand Lazy-load data/assets unless profiling suggests otherwise. Use layered caching: session, view, DB level. Always bound cache size to avoid memory leaks. Pre-generate static pages where possible—static sites are high-efficiency caches. Avoid I/O Use local computation (e.g., HMAC-signed tokens) over DB hits. Encode routing/logic decisions into sessionId/clientId when feasible. Partitioning & Scaling Shard your data; that’s often the bottleneck. Centralize the source of truth; replicate locally. Use multimaster sync (vector clocks, CRDTs) only when essential. Aim for O(log N) operations; allow O(N) preprocessing if needed.

CODEBASE DESIGN Pragmatic Abstraction Use simple, obvious algorithms first—optimize when proven necessary. Producer-side optimization compounds through reuse. Apply the 80/20 rule: optimize for the common case, not the edge. Async & Modular Default to async for side-effectful functions, even if not awaited (in JS). Namespace modules to avoid globals. Autoload code paths on demand to reduce initial complexity. Hooks & Extensibility Use layered architecture: Transport → Controller → Model → Adapter. Add hookable events for observability and customization. Wrap external I/O with middleware/adapters to isolate failures.

SECURITY & INTEGRITY Input Validation & Escaping Validate all untrusted input at the boundary. Sanitize input and escape output to prevent XSS, SQLi, etc. Apply defense-in-depth: validate client-side, then re-validate server-side. Session & Token Security Use HMACs or signatures to validate tokens without needing DB access. Enable secure edge-based filtering (e.g., CDN rules based on token claims). Tamper Resistance Use content-addressable storage to detect object integrity. Append-only logs support auditability and sync.

INTERNATIONALIZATION & ACCESSIBILITY I18n & L10n Externalize all user-visible strings. Use structured translation systems with context-aware keys. Design for RTL (right-to-left) languages and varying plural forms. Accessibility (A11y) Use semantic HTML and ARIA roles where needed. Support keyboard navigation and screen readers. Ensure color contrast and readable fonts in UI design.

GENERAL ENGINEERING PRINCIPLES Idempotency & Replay Handlers should be idempotent where possible. Design for repeatable operations and safe retries. Append-only logs and hashes help with replay and audit. Developer Experience (DX) Provide trace logs, debug UIs, and metrics. Make it easy to fork, override, and simulate environments. Build composable, testable components.

ADDITIONAL TOPICS WORTH COVERING Logging & Observability Use structured logging (JSON, key-value) for easy analysis. Tag logs with request/session IDs. Separate logs by severity (debug/info/warn/error/fatal). Configuration Management Use environment variables for config, not hardcoded values. Support override layers (defaults → env vars → CLI → runtime). Ensure configuration is reloadable without restarting services if possible. Continuous Integration / Delivery Automate tests and checks before merging. Use canary releases and feature flags for safe rollouts. Keep pipelines fast to reduce friction.

ghurtado 6 days ago | parent [-]

> a book I would write about software engineering:

You should probably go do that, rather than using the comment section of HN as a scratch pad of your stream of consciousness. That's not useful to anyone other than yourself.

Is this a copypasta you just have laying around?

MisterMower 6 days ago | parent [-]

On the flip side, his commment actually contributes to the conversation, unlike yours. Poorly written? Sure. You can keep scrolling though.

ghurtado 6 days ago | parent | next [-]

> unlike yours

If irony was a ton of bricks, you'd be dead

motorest 6 days ago | parent | prev [-]

> On the flip side, his commment actually contributes to the conversation (...)

Not really. It goes off on a tangent, and frankly I stopped reading the wall of text because it adds nothing of value.

EGreg 6 days ago | parent [-]

How would you know if it adds nothing of value if you stopped reading it? :)

actionfromafar 6 days ago | parent | next [-]

Here let me attach a copy of Wikipedia. Don’t stop reading! :-)

motorest 6 days ago | parent | prev [-]

> How would you know if it adds nothing of value if you stopped reading it? :)

If you write a wall of text where the first pages are inane drivel, what do you think are the odds that the rest of that wall of text suddenly adds readable gems?

Sometimes a turd is just a turd, and you don't need to analyze all of it to know the best thing to do is to flush it.

EGreg 6 days ago | parent [-]

Every sentence there is meaningful. You can go 1 by 1. But yea the formatting should be better!

motorest 6 days ago | parent [-]

> Every sentence there is meaningful.

It really isn't. There is no point to pretend it is, and even less of a point to expect anyone should waste their time with an unreadable and incoherent wall of text.

You decide how you waste your time, and so does everyone else.

EGreg 6 days ago | parent [-]

For a developers to know

1. Set up a local IDE with a full clone of the app (frontend, backend, DB).

Thus the app must be fully able to run on a small, local environment, which is true of open source apps but not always for for-profit companies

2. Use .env or similar to manage config/secrets; never commit them.

A lot of people don’t properly exclude secrets from version control, leading to catastrophic secret leaks. Also when everyone has their own copy, the developer secrets and credentials aren’t that important.

3. Debuggers and breakpoints are more scalable than console.log. Prefer conditional or version-controlled breakpoints in feature branches.

A lot of people don’t use debuggers and breakpoints, instead doing logging. Also they have no idea how to maintain DIFFERENT sets of breakpoints, which you can do by checking the project files into version control, and varying them by branches.

4. Test & Deployment Environments Maintain at least 3 environments: Local (dev), Staging (integration test), Live (production).

This is fairly standard advice, but it is best practice, so people can test in local and staging.

5. Make state cloning easy (e.g., DB snapshots or test fixtures).

This is not trivial. For example downloading a local copy of a test database, to test your local copy of Facebook with a production-style database. Make it fast, eg by rsync mysql innodb files.