3 ms·
Programmers maybe But software architects (especially of various reusable frameworks) have to maintain the right set of abstractions and make sure the system i
by EGreg 1y 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 1y ago> 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 1y agoOn the flip side, his commment actually contributes to the conversation, unlike yours. Poorly written? Sure. You can keep scrolling though.
- ghurtado 1y ago> unlike yours If irony was a ton of bricks, you'd be dead
- motorest 1y ago> 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 1y agoHow would you know if it adds nothing of value if you stopped reading it? :)
- motorest 1y ago> 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.
- 1y ago