Applies software engineering best practices, design principles, and avoids common anti-patterns. Use when designing systems, reviewing code quality, refactoring legacy code, making architectural decisions, or improving maintainability.
Install
npx skillscat add knoopx/pi/swe Install via the SkillsCat registry.
SKILL.md
Software Engineering
Core Principles
| Principle | Meaning |
|---|---|
| Simplicity | Simplest code that works; complexity only when required |
| Single Responsibility | Each function/class/module does one thing |
| Self-Documenting | Code explains itself; comments are a smell |
| Fail Fast | Validate early, propagate unexpected errors |
| Test Behavior | What code does, not implementation |
| No Backwards Compat | Don't add legacy support unless requested |
| Consistency | Match project conventions over preference |
Design
SOLID
| Principle | Violation Sign |
|---|---|
| Single Responsibility | Class doing too many things |
| Open/Closed | Modifying existing code for new features |
| Liskov Substitution | Overridden methods breaking contracts |
| Interface Segregation | Clients depend on unused methods |
| Dependency Inversion | High-level imports low-level details |
Architecture
Presentation → Application → Domain ← Infrastructure
↓
Dependencies point DOWN onlyRules:
- Domain depends on nothing
- Infrastructure implements domain interfaces
- No circular dependencies
Design Patterns
Use when solving real problems, not preemptively:
| Pattern | Use When |
|---|---|
| Factory | Complex object creation |
| Builder | Many optional parameters |
| Adapter | Incompatible interfaces |
| Facade | Simplifying subsystem |
| Strategy | Runtime algorithm selection |
| Observer | Many dependents need notification |
Security
- Validate all external input (allowlists > denylists)
- Encrypt sensitive data at rest and transit
- Never log secrets
- Parameterized queries (SQL injection)
- Escape output (XSS)
Implementation
Naming
| Type | Convention | Example |
|---|---|---|
| Variables | camelCase noun | userName, isValid |
| Functions | camelCase verb | getUser, validateInput |
| Booleans | is/has/can prefix |
isActive, hasPermission |
| Constants | UPPER_SNAKE | MAX_RETRIES |
| Classes | PascalCase noun | UserService |
Rules:
- Names reveal intent
- No single-letter params
- No abbreviations (
ur→userRepository)
Self-Documenting Code
// ❌ Comment hiding bad code
if (u.r === 1 && u.s !== 0) { ... }
// ✅ Self-documenting
if (user.isAdmin && user.isActive) { ... }Acceptable comments: RFC links, bug tracker refs, non-obvious warnings
Functions
| Do | Don't |
|---|---|
| Small, focused | God functions (100+ lines) |
| 2-3 params max | 6+ parameters |
| Return early | Deep nesting |
| Pure when possible | Hidden side effects |
| Single abstraction level | Mixed levels |
Error Handling
// ❌ Silent catch
try {
await save(user);
} catch (e) {}
// ❌ Log only
try {
await save(user);
} catch (e) {
console.log(e);
}
// ✅ Let propagate or handle specific
try {
await save(user);
} catch (e) {
if (e instanceof DuplicateEmail) return { error: "Email taken" };
throw e;
}Rules:
- Empty catch = always wrong
- Catch only what you can handle
- Re-throw with context or propagate
- Crash visibly > fail silently
File Organization
- Match existing conventions
- No barrel files (
index.tsre-exports) - Import from concrete modules
- Co-locate tests with source
src/users/
user-service.ts
user-service.test.tsCode Smells
| Smell | Fix |
|---|---|
| God Class | Split by responsibility |
| Feature Envy | Move method to data owner |
| Long Param List | Parameter object |
| Primitive Obsession | Value objects |
| Dead Code | Delete it |
Linting
| Tool | Purpose |
|---|---|
| Formatter | Style (Prettier, dprint) |
| Linter | Quality (ESLint, Ruff) |
| Type Checker | Safety (tsc, mypy) |
Rules:
- Automate formatting
- Zero warnings in CI
- Never disable rules—fix the code
Testing
Test Pyramid
E2E (few) - Critical journeys, slow
Integration (some) - Component interactions
Unit (many) - Fast, isolated, business logicWhat to Test
| Test | Skip |
|---|---|
| Business logic | Framework code |
| Edge cases | Trivial getters |
| Error paths | Third-party libs |
| Public API | Private internals |
Test Quality
- Independent and isolated
- Deterministic (no flakiness)
- Fast (< 100ms unit)
- Single reason to fail
- Test behavior, not implementation
BDD Structure
describe("UserService", () => {
describe("given valid data", () => {
describe("when creating user", () => {
it("then persists with ID", async () => {
// arrange, act, assert
});
});
});
});Anti-Patterns
| Pattern | Fix |
|---|---|
| Ice Cream Cone | More unit, fewer E2E |
| Flaky Tests | Fix races, use mocks |
| Testing Implementation | Test behavior |
| No Assertions | Add meaningful checks |
Review
Before PR
- Type check passes
- Lint passes
- Tests pass
- No debug/console.log
- No commented code
- Up to date with main
Review Checklist
Correctness:
- Does it work? Edge cases? Error handling?
Design:
- Right abstraction? SOLID? Dependencies appropriate?
Readability:
- Clear names? Straightforward logic? No unnecessary comments?
Security:
- Input validated? No injection? Secrets handled?
Performance:
- No N+1? No await in loops? Caching considered?
Tests:
- Sufficient coverage? Edge cases? Behavior-focused?
For Reviewers
- Code, not author
- Questions > demands
- Explain the "why"
- Blocking vs nitpick
For Authors
- Small, focused PRs
- Context in description
- Respond to all comments
Maintenance
Refactoring
- Ensure test coverage first
- Small, incremental changes
- Run tests after each change
- Refactor OR add features, never both
| Technique | When |
|---|---|
| Extract Method | Long method, reusable logic |
| Extract Class | Multiple responsibilities |
| Move Method | Uses other class's data more |
| Introduce Param Object | Long parameter lists |
Technical Debt
| Type | Handling |
|---|---|
| Deliberate | Document, schedule payback |
| Accidental | Fix when discovered |
| Bit Rot | Regular maintenance |
| Outdated Deps | Regular updates |
Find: unused code, duplicates, circular deps, outdated deps
Performance
- Don't optimize prematurely
- Measure before optimizing
- Focus on hot paths
| Pitfall | Fix |
|---|---|
| N+1 queries | Batch, joins |
| Blocking I/O | Async |
| Memory leaks | Weak refs, cleanup |
Documentation
| Document | Skip |
|---|---|
| Public APIs | Obvious code |
| ADRs | Implementation details |
| Setup/deploy | Self-documenting code |
| Non-obvious behavior | Every function |
Anti-Patterns
| Pattern | Problem | Fix |
|---|---|---|
| Big Ball of Mud | No structure | Define boundaries |
| Spaghetti Code | Tangled | Modularize |
| Lava Flow | Dead code | Delete it |
| Copy-Paste | Duplication | Extract |
| Magic Numbers | No context | Named constants |
| Circular Deps | Coupling | Abstraction layer |
| Feature Flags | Hidden complexity | One code path |
| Backwards Compat | Legacy burden | Replace entirely |