Markdown Converter
Agent skill for markdown-converter
---
Sign in to like and favorite skills
This document contains universal principles (applicable to all languages) and ecosystem-specific rules.
- Unmarked sections apply universally
- 🐍 = Python/Django specific
- ⚛️ = React Native/TypeScript specific
These principles apply to ALL programming languages and paradigms.
"Simple is better than complex. Complex is better than complicated."
We prefer classes/methods with simple public interfaces that encapsulate complex logic.
| Aspect | Approach |
|---|---|
| Guideline | A method like or a hook like can be complex internally if it offers a simple interface |
| Goal | Reduce cognitive load for the consumer |
| Structure | Public methods = Orchestrators. Private methods = Implementation steps |
"Explicit is better than implicit."
We value typed, predictable return shapes over loose collections.
| Concern | Rule |
|---|---|
| Structured Returns | Never return raw tuples, arrays, or untyped dicts/objects for complex data. Use structured types (dataclasses, interfaces, types) so consumers access data by name, not index. |
| Boundaries | Data crossing module boundaries should be structured DTOs, decoupling internal models from public contracts. |
"Errors should never pass silently. Unless explicitly silenced."
We prefer clear, domain-specific failures over generic technical crashes.
| Concern | Rule |
|---|---|
| Translation | Catch low-level technical errors at the boundary and re-raise/transform them as Domain Errors that callers can understand. |
| Granularity | Do not catch all exceptions blindly. Only catch what you can handle or need to wrap. |
"In the face of ambiguity, refuse the temptation to guess."
Before proposing fixes, validate your assumptions about root cause. This applies to ALL languages.
| Concern | Rule |
|---|---|
| Symptom ≠ Cause | An error message describes WHAT failed, not WHY. Never assume the cause. |
| Hypothesis First | State your hypothesis explicitly before investigating: "I suspect X because Y" |
| Validation Required | Use evidence (logs, traces, reproduction, code inspection) to confirm or refute hypothesis BEFORE coding a fix |
| Minimal Reproduction | Isolate the failure to the smallest possible scope before debugging |
# BAD: Jumped to conclusion Symptom: "Cannot read property 'name' of undefined" Assumption: "user must be undefined" Fix without validation: Add null check blindly # GOOD: Validated first Symptom: "Cannot read property 'name' of undefined" Hypothesis: "user might be undefined when session expires" Validation: Added logging, found user exists but user.profile is undefined Actual fix: Check user.profile, not just user
Unlike Python's "Zen," React relies on structural constraints.
| Philosophy | Meaning in Practice |
|---|---|
| UI = f(State) | The interface is a pure function of your data. Never manually update a text box; update the state variable, and the box updates itself. |
| Declarative > Imperative | Describe what the UI should look like now, not how to change it from the previous state. |
| Unidirectional Data Flow | Data flows down (Props). Events flow up (Callbacks). Never try to "reach up" and modify a parent directly. |
| Composition > Inheritance | Never subclass Components. Build complex UIs by wrapping simple components inside container components. |
| Colocation | Keep logic, styles, and tests close to the component that uses them. Don't scatter files by type. |
| Principle | Description |
|---|---|
| Functional Components Only | Class components are legacy. Use Functional Components with Hooks. |
| Render Purity | The body of a component function must be pure. No side effects (API calls, subscriptions) in the render body; put them in or event handlers. |
| Props are Read-Only | A component must never modify its own props. |
| Principle | Description |
|---|---|
| Lift State Up | If two components need the same data, move the state to their nearest common ancestor. |
| Server vs. Client State | Crucial: Do not treat API data the same as UI state (like ). Use a dedicated async state manager (TanStack Query) for server data. |
| Single Source of Truth | Avoid "Derived State" in storage. If you have and , do not store . Calculate it on the fly during render. |
In Python, we use Classes to hide complexity. In React, we use Custom Hooks.
Components should focus on Layout (Visuals). Logic should be extracted to Hooks.
| Aspect | Rule |
|---|---|
| Separation | If a component has more than 3 calls or complex logic, extract it into a custom hook (e.g., ). |
| The Interface | The Hook returns an object containing data and handlers. The Component just wires them to the UI. |
| Benefit | This makes logic testable in isolation from the UI rendering. |
The equivalent of "No Tuples" in Python.
| Rule | Description |
|---|---|
| Named Parameters (Objects) | In JavaScript functions (and Component Props), always pass objects, not positional arguments. Bad: . Good: |
| Discriminated Unions | Never use boolean flags for multi-state logic. Bad: . Good: `status: 'loading' |
| No Prop Drilling | If you are passing data through more than 2 layers of components that don't use it, use Context or Composition. |
JavaScript is non-blocking, which creates race conditions Python devs rarely face.
| Concern | Rule |
|---|---|
| Race Conditions | When fetching data in a , ensure the component is still mounted before setting state, or use a library (TanStack Query) that handles cancellation automatically. |
| Loading States | Every async action must have a visible Loading state and Error state in the UI. |
| Optimistic Updates | For high-quality feel, update the UI before the server responds (then rollback on error). |
| Concern | Rule |
|---|---|
| Flexbox Only | Never use hardcoded Dimensions (pixels) for layout structure. Use Flexbox (, ). Screens are different sizes! |
| Memoization | If a child component re-renders too often, use . Wrap heavy functions in . |
| FlatList Hygiene | Never use for long lists. Use to recycle memory. |
// BAD: Fat Component (Logic mixed with View) // Violation: Component knows too much about fetching and transforming data const UserProfile = ({ userId }) => { const [user, setUser] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { api.fetchUser(userId).then(u => { setUser(u); setLoading(false); }); }, [userId]); if (loading) return <Spinner />; return <Text>{user.name}</Text>; }; // GOOD: Logic Encapsulation // 1. The Logic (The "Deep Module") const useUserProfile = (userId: string) => { // Uses a library like TanStack Query for caching/loading states const { data, isLoading, error } = useQuery({ queryKey: ['user', userId], queryFn: () => api.fetchUser(userId) }); return { user: data, isLoading, isError: !!error }; }; // 2. The View (Pure UI) const UserProfile = ({ userId }: { userId: string }) => { const { user, isLoading, isError } = useUserProfile(userId); if (isLoading) return <Spinner />; if (isError) return <ErrorView />; return <Text>{user.name}</Text>; };
// BAD: Positional Arguments (The Tuple Anti-Pattern) // Violation: What does 'true' mean? What is 500? // If I change the order, everything breaks. function createAnimation(ref, 500, true, 'easeIn') { ... } // GOOD: Options Object // Standard JS Pattern: "Roving Object" interface AnimationConfig { duration: number; useNativeDriver: boolean; easing?: 'easeIn' | 'easeOut'; } function createAnimation(ref: any, config: AnimationConfig) { ... } // Usage is self-documenting createAnimation(myRef, { duration: 500, useNativeDriver: true });
// BAD: Boolean Soup // Violation: Impossible states (e.g., loading=true AND error=true) interface State { isLoading: boolean; isError: boolean; data: User | null; error: string | null; } // GOOD: Mathematical State // The compiler ensures you handle every case type State = | { status: 'idle' } | { status: 'loading' } | { status: 'success'; data: User } | { status: 'error'; error: string }; // Usage if (state.status === 'success') { console.log(state.data.name); // TS knows 'data' exists here }
// BAD: Redundant State const [items, setItems] = useState([1, 2, 3]); // Violation: 'count' is redundant. If 'items' updates, 'count' might drift. const [count, setCount] = useState(3); // GOOD: Calculated Values const [items, setItems] = useState([1, 2, 3]); // 'count' is calculated on every render. It cannot be out of sync. const count = items.length;
Beautiful is better than ugly. Explicit is better than implicit. Simple is better than complex. Complex is better than complicated. Flat is better than nested. Sparse is better than dense. Readability counts. Special cases aren't special enough to break the rules. Although practicality beats purity. Errors should never pass silently. Unless explicitly silenced. In the face of ambiguity, refuse the temptation to guess. There should be one-- and preferably only one --obvious way to do it. Although that way may not be obvious at first unless you're Dutch. Now is better than never. Although never is often better than *right* now. If the implementation is hard to explain, it's a bad idea. If the implementation is easy to explain, it may be a good idea. Namespaces are one honking great idea -- let's do more of those!
| Principle | Description |
|---|---|
| Loose Coupling | Layers shouldn't "know" about each other unless necessary. Template system knows nothing about web requests; database layer knows nothing about display |
| Less Code | Apps should lack boilerplate. Take advantage of Python's dynamic capabilities |
| Quick Development | Make tedious aspects of web development fast |
| DRY | Every concept/data should live in one place. Redundancy is bad. Normalization is good |
| Explicit > Implicit | No "magic" unless it creates huge convenience unattainable otherwise |
| Consistency | Consistent at all levels—coding style to user experience |
| Principle | Description |
|---|---|
| Explicit > Implicit | Fields shouldn't assume behaviors based on name. Use keyword arguments |
| Include All Domain Logic | Models encapsulate every aspect of an "object" (Active Record pattern). All info needed to understand a model should be stored in the model |
| Principle | Description |
|---|---|
| SQL Efficiency | Execute SQL as few times as possible. Optimize internally. Explicit calls |
| Terse, Powerful Syntax | Rich statements in minimal syntax. Automatic joins when needed |
| Raw SQL Escape Hatch | Easy to write custom SQL when the ORM isn't enough |
| Principle | Description |
|---|---|
| Simplicity | Writing a view = writing a Python function. No class instantiation when function suffices |
| Request Objects | Pass request directly to view (not global). Makes testing easy |
| GET vs POST | Explicitly distinguish between them |
"Explicit is better than implicit. Errors should never pass silently."
While Django is stateful, we minimize side effects in business logic.
| Rule | Description |
|---|---|
| Look, Don't Touch | Do not mutate Model instances unless the function's explicit purpose is mutation |
| Isolate IO | Separate pure logic (transformations) from IO (saving, emailing) |
| Immutability | Prefer Pydantic models or dicts over raw Django models when mutation isn't required |
"There should be one-- and preferably only one --obvious way to do it."
The Database is the source of truth, not Python.
| Concern | Rule |
|---|---|
| Enums | ALWAYS use for status/types. NEVER use string literals |
| Constraints | ALWAYS use and in Meta |
| Transactions | ALWAYS wrap multi-step DB writes in |
| Deletes | Default to . Use only for strict ownership |
"Readability counts. Explicit is better than implicit."
| Concern | Rule |
|---|---|
| Typing | Strictly typed signatures: . |
| Naming | Descriptive names ( > ). Named kwargs for complex calls. |
| Model-Centric Logic | Models are the entry point for logic. If a method exceeds ~20 lines or requires complex imports, extract it to a standalone Service or Selector class. |
| Bulk Ops | Use /. Never save in a loop. |
# BAD: Implicit mutation and mixed concerns # Violates: "Explicit is better than implicit" def check_status(job): if job.is_stuck: job.status = 'failed' # Side effect job.save() # IO inside logic return False return True # GOOD: Pure logic + Explicit IO # Follows: "Simple is better than complex" def get_job_status(job) -> Status: if job.is_stuck: return Status.FAILED return Status.RUNNING # Caller handles the mutation explicitly new_status = get_job_status(job) if new_status != job.status: job.status = new_status job.save(update_fields=['status'])
# Follows: "Include all relevant domain logic" (Django Models philosophy) # Follows: "Simple is better than complex" class DbtProject(models.Model): def build(self): """ Deep Method: Simple interface, complex execution. Acts as a 'Table of Contents' for the operation. """ # Note: If these methods are huge, they delegate to a Service class self._load_configs() self._run_dbt_cli() self._update_nodes() self._generate_flows()
# BAD: "Mystery Meat" Return # Violates: "Explicit is better than implicit" # Problem: Consumer must memorize that index 1 is conversion_rate def get_dashboard_metrics(): return 120, 45.5, True # GOOD: Structured Contract # Follows: "Data Contracts" from dataclasses import dataclass @dataclass(frozen=True) class DashboardMetrics: total_users: int conversion_rate: float is_feature_flag_enabled: bool def get_dashboard_metrics() -> DashboardMetrics: return DashboardMetrics( total_users=120, conversion_rate=45.5, is_feature_flag_enabled=True )
# BAD: Leaking Implementation Details # Violates: "Simplicity" (The view has to know about Database integrity) def register_user(email): # This might raise IntegrityError if email exists, crashing the view User.objects.create(email=email) # GOOD: Domain Language # Follows: "Semantic Failures" class UserAlreadyExists(Exception): pass def register_user(email): try: User.objects.create(email=email) except IntegrityError: # Translate technical error to business error raise UserAlreadyExists(f"User with email {email} already exists")
# GOOD: Database enforces integrity # Follows: "Errors should never pass silently" class Plot(models.Model): class Status(models.TextChoices): ACTIVE = 'active', 'Active' ARCHIVED = 'archived', 'Archived' status = models.CharField(max_length=20, choices=Status.choices) map_area = models.FloatField() class Meta: constraints = [ models.CheckConstraint( check=models.Q(map_area__gt=0), name='positive_map_area' ), ]
# BAD: N+1 saves # Violates: "SQL Efficiency" (Django Database API philosophy) for item in items: obj = MyModel(name=item['name']) obj.save() # GOOD: Single bulk insert # Follows: "Less code", "SQL Efficiency" MyModel.objects.bulk_create([ MyModel(name=item['name']) for item in items ])
# GOOD: Atomic multi-step operation # Follows: "Errors should never pass silently" from django.db import transaction def transfer_ownership(plot, new_owner): with transaction.atomic(): old_owner = plot.owner plot.owner = new_owner plot.save(update_fields=['owner']) OwnershipLog.objects.create( plot=plot, from_owner=old_owner, to_owner=new_owner )