- Strong preference for functional programming patterns
- Use
map,filter,reduceextensively rather than imperative loops - Favor immutable data transformations over mutation
- Create pure functions where possible
- Use function composition and higher-order functions
- Prefer building the "piping" or "system" that data can flow through, making it easy to test and reason about
- Implement robust error handling with multiple fallback strategies
- Always include comprehensive try-catch blocks in async operations
- Log meaningful error messages with context
- Prefer graceful degradation over hard failures
- Create small, focused functions with single responsibilities
- Extract reusable utilities into generic functions
- Use descriptive variable names but keep them concise
- Prioritize readability and maintainability over cleverness
- Dont overuse barrel imports
- Use type annotations but rely on inference when types are obvious
- Prefer to keep things out of react and hooks unless absolutely necesssary
- I despise useEffect, try to only use with empty or very little dependencies. useUeffect usage should not depend on dependencies mutating
- Use conditional rendering patterns effectively
- Implement proper error handling for all async operations
Readable, staged TypeScript. Dense but visually paragraphed, with comments used as operational memory, not decoration.
Code should feel like data moving through a small system: gather inputs, normalize, transform, execute, finalize.
- Use one blank line to separate ideas.
- Do not use multiple blank lines as padding.
- Group code into visible phases: setup, derived values, validation, execution, cleanup.
- Prefer named intermediate values over dense inline expressions.
- Long functions are acceptable when each phase is visually obvious.
- Extract helpers when they create a real boundary or reusable transformation. Do not extract tiny one-off helpers just to hide two lines.
const messages = await buildMessages(children, {
includeImages: true,
})
const request = {
model,
messages,
timeoutMs,
}
const requestToLog = cleanRequest(request)
logger.info('Request started', requestToLog)
const response = await client.send(request)
return normalizeResponse(response)- Conditions should read like predicates.
- Always use braces.
- Put the body on its own lines.
- Prefer multiline conditions when any part is non-trivial.
- Never assign or do meaningful work inside a condition.
- Compute named values first, then branch on them.
const hasContent = message.parts.some((part) => {
if (part.type === 'text') {
return part.text.length > 0
}
return part.type === 'image'
})
if (!hasContent) {
return null
}if (event.type === 'content' && event.delta.type === 'text') {
output += event.delta.text
yield event.delta.text
}Avoid this shape:
if ((chunk = event.next()) && chunk.text) {
yield chunk.text
}Prefer:
const chunk = event.next()
const hasText = chunk && chunk.text
if (hasText) {
yield chunk.text
}- Prefer early returns over nested conditionals.
- Keep immediate guards attached to their setup variables; no blank line is needed between initialization and an early return.
- Put the blank line after the guard, before the next phase.
- Put one blank line before the final success return when it follows setup or derived variables.
- Use
continuewhen it keeps the main path flat. - Use
switchwhen branching over a small tagged domain. - Use loops when order, async iteration, cleanup, or short-circuiting matter.
- Use
map,filter, andreducefor data conversion boundaries.
if (!client) {
throw new Error('Missing client')
}
if (!items.length) {
return []
}
return items
.filter((item) => item.enabled)
.map((item) => ({
id: item.id,
label: item.name.trim(),
}))const id = request.param('id')
const session = await getSession(id)
if (!session) {
return jsonResponse({ error: 'session not found' }, 404)
}
const payload = await buildPayload(session)
return jsonResponse(payload)for await (const event of stream) {
if (event.type === 'metadata') {
metadata = event.value
continue
}
if (event.type === 'content') {
output += event.text
yield event.text
}
}Comments explain hidden context, not obvious code.
Use comments for:
- provider/API quirks
- async lifecycle traps
- TypeScript limitations
- intentional deviations
- retry/fallback reasoning
- examples of external payloads
- uncertainty worth preserving
// The remote API returns 200 with an empty payload for filtered content.
// Treat that as a non-retryable refusal so callers can handle it explicitly.
if (text.length === 0 && finishReason === 'filtered') {
throw new CompletionError('Filtered response', false)
}// TypeScript can't infer that this value is initialized by attachStream().
// Keep the runtime check so misuse fails at the boundary.
if (!this.stream) {
throw new Error('Stream not attached')
}Rough internal notes are fine when they encode useful history:
// TODO(al): this probably wants a typed span later
// maybe split request metadata from response metadataAvoid comments that only narrate syntax:
// Increment count by one
count += 1- Wrap async/provider boundaries with explicit error handling.
- Preserve original errors when useful.
- Prefer graceful degradation when the caller can continue.
- Log with enough context to debug without re-running locally.
try {
const result = await fetchImage(url)
return {
success: true,
data: result,
}
} catch (error) {
logger.warn(`Failed to fetch image (url=${url})`, error)
if (failOnError) {
throw error
}
return {
success: false,
fallbackText: `[Image unavailable: ${url}]`,
}
}- Let types document the shape of the system.
- Use tagged unions for state and results.
- Put short comments on union variants when they clarify meaning.
- Avoid
anyunless the boundary truly requires it.
type ImageData =
// from URL, provider can fetch directly
| {
url: string
data: null
mediaType: null
}
// downloaded locally
| {
url: string
data: string
mediaType: ImageMediaType
}
// caller supplied base64
| {
url: null
data: string
mediaType: ImageMediaType
}- Top-level imports only.
- Keep object literals and function calls vertically readable.
- Prefer explicit object construction over clever spreading when behavior would be unclear.
const payload = {
id,
status,
metadata: {
source,
retryCount,
},
}- Can the reader see the phases from blank lines alone?
- Do conditions read as predicates?
- Are all branches brace-delimited and multiline?
- Is any assignment hidden inside a condition?
- Are comments explaining context instead of syntax?
- Are async/provider boundaries wrapped with useful errors?
- Is data conversion mostly functional and control flow mostly flat?
- Add logging at key points to trace execution flow
- Use descriptive console.log messages with context
- Implement debugging utilities when working with complex data flows
- Create helper functions for common debugging tasks
-
Always look for opportunities to reduce complexity
-
Extract common patterns into reusable utilities
-
Prefer declarative over imperative code
-
Remove unnecessary nesting and conditionals Abstraction is acceptable, even preferable, when it makes the system more:
-
understandable
-
predictable
-
reusable/composable
-
testable
-
easier to reason about as data flowing through a system
Not abstraction for abstraction’s sake, and not tiny helpers for one call site. More like: build a small, coherent system boundary/pipeline if it makes behavior easier to verify and extend.
- Write code that is easy to test and debug
- Create modular, composable functions
- Use consistent patterns across the codebase
- Document complex logic with clear comments
- Always suggest the most maintainable solution
- Provide functional alternatives when appropriate
- Include proper error handling in all async operations
- Explain trade-offs between different approaches
- Focus on creating reusable, composable solutions
- Consider performance implications of suggested solutions
- When creating branches, prefix them with al/ to indicate they came from me.