Write production-safe code that survives maintenance — avoid the 15 patterns AI agents commonly generate that work on day 1 but break 3 months later; enforce error handling, logging, type safety, no magic values, and testable structure before writing any code.
Scanned 9/9/2026
Install to Claude Code
npx -y skills add yanacuti1121/Yana-AI --skill ai-code-maintainability --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Ai Code Maintainability?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/yanacuti1121-ai-code-maintainability)More formats (shields.io, HTML) on the badges page.
---
name: ai-code-maintainability
description: Write production-safe code that survives maintenance — avoid the 15 patterns AI agents commonly generate that work on day 1 but break 3 months later; enforce error handling, logging, type safety, no magic values, and testable structure before writing any code.
triggers:
- "ai code maintenance"
- "code maintainability"
- "production ready code"
- "ai generated code problems"
- "code quality gate"
- "avoid technical debt"
- "code review checklist"
- "prevent maintenance bugs"
- "ai code anti-patterns"
- "sustainable code"
- "code that lasts"
- "avoid magic numbers"
- "proper error handling"
do_not_use_for:
- Quick prototypes explicitly marked as throwaway
- Test fixtures and mock data
see_also:
- verification
- golden-principles
- agent-code-constraints
---
# AI Code Maintainability — Write Code That Survives
**The core problem:** AI agents optimize for "works on first run." Maintenance bugs appear 3–6 months later when context is gone, requirements shift, or edge cases hit prod.
---
## The 15 Patterns That Kill Maintainability
### 1. Silent Error Swallowing (most dangerous)
```python
# ❌ AI generates this — crashes silently in prod
try:
result = call_external_api(data)
except:
pass
# ✅ Always log + re-raise or return error state
import logging
logger = logging.getLogger(__name__)
try:
result = call_external_api(data)
except ExternalAPIError as e:
logger.error("API call failed: %s | data=%s", e, data, exc_info=True)
raise # or: return Result.error(str(e))
except Exception as e:
logger.critical("Unexpected error in call_external_api: %s", e, exc_info=True)
raise
```
### 2. Bare `except` / Pokémon Exception Handling
```python
# ❌ Catches KeyboardInterrupt, SystemExit, MemoryError — everything
try:
do_work()
except Exception: # still too broad
return None # swallowed silently
# ✅ Catch specific exceptions, handle each explicitly
try:
do_work()
except ValueError as e:
logger.warning("Invalid input: %s", e)
return default_value
except NetworkError as e:
logger.error("Network failure: %s", e)
raise RetryableError(f"Network unavailable: {e}") from e
```
### 3. Magic Numbers and Strings
```python
# ❌ What is 300? What is "active"?
if user.age > 300:
status = "active"
time.sleep(300)
# ✅ Named constants with explanations
MAX_VALID_AGE_YEARS = 150 # ISO 8601 human lifespan upper bound
ACTIVE_STATUS = "active"
CACHE_REFRESH_INTERVAL_SECONDS = 300 # 5-minute cache TTL per SLA
if user.age > MAX_VALID_AGE_YEARS:
status = ACTIVE_STATUS
time.sleep(CACHE_REFRESH_INTERVAL_SECONDS)
```
### 4. print() Instead of Structured Logging
```python
# ❌ No timestamp, no level, no context, can't filter in prod
print(f"Processing user {user_id}")
print("ERROR: something failed")
# ✅ Structured logging — searchable in Datadog/CloudWatch/Grafana
import logging
logger = logging.getLogger(__name__)
logger.info("Processing user", extra={"user_id": user_id, "action": "process"})
logger.error("Processing failed", extra={"user_id": user_id}, exc_info=True)
```
### 5. Missing Type Hints (Python / TypeScript)
```python
# ❌ Future maintainer has no idea what this accepts/returns
def process_data(data, config, mode=None):
...
# ✅ Self-documenting — IDE and mypy catch errors immediately
from typing import Optional
from dataclasses import dataclass
def process_data(
data: list[dict[str, str]],
config: ProcessConfig,
mode: Optional[ProcessMode] = None,
) -> ProcessResult:
...
```
```typescript
// ❌ any disables the type system
function processUser(data: any): any {
return data.profile.name;
}
// ✅ Full types — TypeScript catches data.profile?.name at compile time
interface User { id: string; profile: { name: string; email: string } }
interface ProcessResult { displayName: string; emailHash: string }
function processUser(data: User): ProcessResult {
return { displayName: data.profile.name, emailHash: hash(data.profile.email) };
}
```
### 6. Mutable Default Arguments (Python silent bug)
```python
# ❌ Classic Python bug — list shared across ALL calls
def add_item(item, items=[]): # same [] object forever
items.append(item)
return items
add_item("a") # ["a"]
add_item("b") # ["a", "b"] ← not ["b"]!
# ✅ None sentinel pattern
def add_item(item: str, items: list[str] | None = None) -> list[str]:
if items is None:
items = []
items.append(item)
return items
```
### 7. Hardcoded Paths and Environment Values
```python
# ❌ Breaks in Docker, in CI, on another dev's machine
DATA_PATH = "/home/user/projects/myapp/data"
API_URL = "http://localhost:8000"
# ✅ Environment-driven — works everywhere
import os
from pathlib import Path
DATA_PATH = Path(os.environ.get("DATA_PATH", "./data")).resolve()
API_URL = os.environ.get("API_URL", "http://localhost:8000")
if not os.environ.get("API_URL"):
raise EnvironmentError("API_URL must be set. Example: export API_URL=https://api.prod.com")
```
### 8. No Retry / Timeout on External Calls
```python
# ❌ Hangs forever if service is slow; crashes on first transient error
response = requests.get(f"{API_URL}/users/{user_id}")
data = response.json()
# ✅ Timeout + retry with exponential backoff
import requests
from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type
@retry(
retry=retry_if_exception_type((requests.Timeout, requests.ConnectionError)),
wait=wait_exponential(multiplier=1, min=1, max=30),
stop=stop_after_attempt(3),
)
def fetch_user(user_id: str) -> dict:
response = requests.get(
f"{API_URL}/users/{user_id}",
timeout=(3.05, 27), # (connect_timeout, read_timeout)
)
response.raise_for_status()
return response.json()
```
### 9. Unvalidated External Input
```python
# ❌ Crashes or does wrong thing on unexpected input
def create_user(data: dict) -> User:
return User(
name=data["name"],
email=data["email"],
age=data["age"],
)
# ✅ Validate at system boundary — fail fast with clear message
from pydantic import BaseModel, EmailStr, Field
class CreateUserRequest(BaseModel):
name: str = Field(min_length=1, max_length=100)
email: EmailStr
age: int = Field(ge=0, le=150)
def create_user(raw_data: dict) -> User:
req = CreateUserRequest.model_validate(raw_data) # raises ValidationError with details
return User(name=req.name, email=req.email, age=req.age)
```
### 10. Uncaught Promise Rejections (TypeScript/JS)
```typescript
// ❌ Unhandled rejection crashes Node.js silently in some versions
async function loadData() {
const data = await fetchFromAPI(); // what if this throws?
return data;
}
// Caller: loadData() ← no await, no .catch()
// ✅ Always handle async errors
async function loadData(): Promise<Result<Data, AppError>> {
try {
const data = await fetchFromAPI();
return { ok: true, data };
} catch (error) {
logger.error("Failed to load data", { error });
return { ok: false, error: new AppError("DATA_LOAD_FAILED", String(error)) };
}
}
// Caller always handles:
const result = await loadData();
if (!result.ok) return handleError(result.error);
```
### 11. Resource Leaks (File/DB/Connection)
```python
# ❌ File not closed if exception thrown
f = open("data.csv")
data = f.read()
process(data)
f.close() # never reached if process() throws
# ✅ Context manager guarantees cleanup
with open("data.csv", "r", encoding="utf-8") as f:
data = f.read()
# ✅ Async version
async with aiofiles.open("data.csv") as f:
data = await f.read()
```
### 12. N+1 Query Problem (AI loves loops over DB)
```python
# ❌ N queries for N users — kills prod at scale
users = db.query(User).all()
for user in users:
orders = db.query(Order).filter(Order.user_id == user.id).all() # N queries
send_summary(user, orders)
# ✅ Eager load with JOIN — 1 query total
from sqlalchemy.orm import joinedload
users = db.query(User).options(joinedload(User.orders)).all()
for user in users:
send_summary(user, user.orders) # already loaded
```
### 13. Sleeping Instead of Polling with Backoff
```python
# ❌ Fixed sleep — either too fast (hammers API) or too slow
while True:
status = check_job_status(job_id)
if status == "done":
break
time.sleep(5)
# ✅ Exponential backoff with max cap
import time
def wait_for_job(job_id: str, max_wait: int = 300) -> JobResult:
delay = 1
elapsed = 0
while elapsed < max_wait:
status = check_job_status(job_id)
if status.is_terminal():
return status
logger.debug("Job %s status=%s, retrying in %ds", job_id, status, delay)
time.sleep(delay)
elapsed += delay
delay = min(delay * 2, 30) # cap at 30s
raise TimeoutError(f"Job {job_id} did not complete within {max_wait}s")
```
### 14. Implicit Dependencies Between Modules
```python
# ❌ Module A depends on global state set by module B — breaks on import order change
# in config.py
DATABASE_URL = None
# in main.py
from config import *
DATABASE_URL = os.environ["DATABASE_URL"] # set globally
# in users.py — only works if main.py ran first
from config import DATABASE_URL
db = connect(DATABASE_URL) # None if imported before main.py init
# ✅ Explicit dependency injection
class UserRepository:
def __init__(self, db_url: str):
self.db = connect(db_url)
# main.py wires everything
db_url = os.environ["DATABASE_URL"]
user_repo = UserRepository(db_url=db_url)
```
### 15. No Idempotency on Write Operations
```python
# ❌ Double-click / retry creates duplicate records
def create_order(user_id: str, items: list) -> Order:
order = Order(user_id=user_id, items=items)
db.add(order)
db.commit()
return order
# ✅ Idempotency key prevents duplicates
def create_order(user_id: str, items: list, idempotency_key: str) -> Order:
existing = db.query(Order).filter(
Order.idempotency_key == idempotency_key
).first()
if existing:
return existing # same result, no duplicate
order = Order(user_id=user_id, items=items, idempotency_key=idempotency_key)
db.add(order)
db.commit()
return order
```
---
## Pre-Write Checklist (run mentally before every function)
```
□ What happens when the external call fails? (error path written?)
□ What happens with empty input / null / zero?
□ Will this work in 6 months without the original context? (self-documenting?)
□ Are all "magic" values named constants?
□ Does every exception get logged with context?
□ Are all required env vars validated at startup, not at use time?
□ If this function is called twice, does it produce the same result?
□ Can this be tested without the real DB/API/filesystem?
```
---
## Self-Review Protocol (before handing off code)
```python
# Run this mental filter on every file written:
MAINTAINABILITY_CHECKLIST = {
"error_handling": "Every try block has a specific except with logging",
"type_safety": "All function params and returns are typed",
"constants": "No unexplained numeric or string literals",
"logging": "Structured logger used, not print()",
"env_config": "All config comes from env vars with startup validation",
"timeouts": "All external calls have explicit timeout values",
"idempotency": "Write operations are safe to retry",
"resource_mgmt": "All file/db/connection handles use context managers",
"no_globals": "No mutable global state; deps injected explicitly",
"testability": "Can I unit-test this without real external systems?",
}
```
---
## Anti-Fake-Pass Checks
- [ ] Code runs ≠ code is production-safe — always apply checklist
- [ ] Catching `Exception` is still too broad for most cases — catch specific types
- [ ] `logger.error("failed")` with no context is nearly useless — log IDs, inputs, stack
- [ ] Type hints on just the happy path but `Optional` not handled = hidden None crashes
- [ ] Passing tests doesn't mean idempotency — tests usually don't test double-submit
- [ ] "Works in dev" with env vars from `.env` file ≠ works in prod with missing vars
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!