Code Reusability Standards
Before you build anything — check if it already exists. When to extract to a shared component, hook, service, or function.
Core rule:
Before you build anything, ask: "Does this already exist, or will it exist again?" If the answer is yes to either — build it once, build it properly, and reuse it everywhere.
Contents
| # | Section |
|---|---|
| 1 | Reusability Check — Before You Build |
| 2 | The Decision Rule |
| 3 | Platform-Specific Guide (React / WeWeb / Xano / Django / Supabase) |
| 4 | Naming Conventions |
| 5 | Anti-Patterns |
| 6 | When NOT to Over-Abstract |
| 7 | Checklist Before Marking Done |
1. The Reusability Check — Before You Start Building
Run this check on every new task before writing a single line:
If any answer is yes — stop. Extract before you build.
2. The Reusability Decision Rule
| Situation | Decision |
|---|---|
| Used in 2 or more places right now | Must be reusable |
| Will clearly be used again soon | Build reusable from the start |
| Complex logic with a clear purpose | Extract to a named function |
| You're copy-pasting from another file | Stop. Extract first. |
| Identical UI component used on two pages | Shared component |
| Same API call pattern in multiple files | Shared service/hook |
| Same validation rules in Add and Edit | Shared validation schema |
| Same error handling logic repeated | Shared error handler |
| Three or more similar lines doing the same pattern | Extract to function |
Three or more similar lines = automatic extraction.
3. Platform-Specific Reusability Guide
React / Next.js
What to Make Reusable
| Item | Where It Lives | Example |
|---|---|---|
| Shared UI component | components/ui/ |
Button.tsx, Input.tsx, Modal.tsx |
| Feature-specific component | components/[feature]/ |
ProjectCard.tsx, MemberList.tsx |
| Shared form | components/forms/ |
ProjectForm.tsx (used in Add + Edit) |
| Custom hook | hooks/ |
useProjects.ts, useAuth.ts |
| API call logic | services/ or lib/ |
projects.service.ts |
| Validation schema | validations/ |
project.schema.ts |
| Error handler | utils/api-error-handler.ts |
Used across all API calls |
| Constants and enums | constants/ |
roles.ts, status.ts |
| Type definitions | types/ |
Project.ts, User.ts |
Folder Structure
src/
components/
ui/
Button.tsx
Input.tsx
Modal.tsx
Table.tsx
Badge.tsx
forms/
ProjectForm.tsx ← Used in Add Project AND Edit Project
MemberInviteForm.tsx
modals/
DeleteConfirmModal.tsx
ConfirmActionModal.tsx
hooks/
useProjects.ts
useWorkspace.ts
useAuth.ts
services/
projects.service.ts
members.service.ts
auth.service.ts
validations/
project.schema.ts
member.schema.ts
utils/
api-error-handler.ts
date-formatter.ts
string-utils.ts
constants/
roles.ts
status.ts
routes.ts
types/
project.ts
user.ts
workspace.ts
Anti-Patterns
× Same Button component defined in 5 different files.
× ProjectForm duplicated in AddProject.tsx and EditProject.tsx with different validation.
× fetch('/api/projects') called directly in 8 different components.
× Same error handling logic copy-pasted across every form submit handler.
× Validation rules written inline in each form component.
WeWeb
What to Make Reusable
| Item | Where It Lives |
|---|---|
| Header / Navbar | Reusable Element |
| Footer | Reusable Element |
| Sidebar | Reusable Element |
| Card component | Reusable Element |
| Form section | Reusable Element |
| Modal / Popup | Reusable Element |
| API call | Named Collection or Action |
| Global variables | WeWeb Global Variables |
| Color/typography | WeWeb Global Design Styles |
WeWeb Reusability Rules
Naming Reusable Elements
| Good | Bad |
|---|---|
Card — Project |
Group 4 |
Modal — Delete Confirmation |
Popup copy |
Header — Authenticated |
Header thing |
Form — Invite Member |
Section 2 |
Xano
What to Make Reusable
| Item | Where It Lives |
|---|---|
| Auth/permission check | Xano Function: check_workspace_permission |
| Slug generator | Xano Function: generate_slug |
| Email sender | Xano Function: send_email |
| Date formatter | Xano Function: format_date |
| Price calculator | Xano Function: calculate_invoice_total |
| Pagination logic | Xano Function: paginate_query |
| Webhook signature check | Xano Function: verify_webhook_signature |
Rule
If you write the same 3+ steps in two different Xano endpoints — extract to a function.
Django
What to Make Reusable
| Item | Where It Lives | Example | |
|---|---|---|---|
| Business logic | services.py or managers.py |
ProjectService.create_project() |
|
| Reusable queries | managers.py |
Project.objects.active() |
|
| Permission mixin | mixins.py |
WorkspaceMemberMixin |
|
| Reusable form | forms.py |
ProjectForm (used in create + update views) |
|
| Template component | templates/components/ |
card.html, form_field.html |
|
| Template filter | templatetags/ |
`{{ date | format_date }}` |
| Shared validation | validators.py |
validate_phone_number() |
|
| Utility functions | utils.py |
generate_slug(), send_notification() |
Fat Models, Thin Views Rule
View: Handle the HTTP request and response. Not more.
Model/Service: Handle the business logic.
Form/Serializer: Handle validation.
Template: Handle display only.
Bad — business logic in view:
def create_project(request):
name = request.POST.get('name')
slug = name.lower().replace(' ', '-')
existing = Project.objects.filter(slug=slug, workspace=workspace).first()
if existing:
...
Project.objects.create(name=name, slug=slug, workspace=workspace, created_by=request.user)
send_email(...)
log_audit(...)
Good — logic extracted to service:
# services/project_service.py
class ProjectService:
@staticmethod
def create_project(workspace, name, created_by):
slug = generate_slug(name)
if Project.objects.filter(slug=slug, workspace=workspace).exists():
raise ConflictError('Project name already exists.')
project = Project.objects.create(name=name, slug=slug, workspace=workspace, created_by=created_by)
send_notification(created_by, 'project_created', project)
AuditLog.log('project.created', actor=created_by, entity=project)
return project
# views.py
def create_project(request):
form = ProjectForm(request.POST)
if not form.is_valid():
return error_response(form.errors)
project = ProjectService.create_project(
workspace=request.workspace,
name=form.cleaned_data['name'],
created_by=request.user
)
return success_response(project)
Supabase
What to Make Reusable
| Item | Where It Lives |
|---|---|
| Complex multi-step operations | RPC / Postgres Function |
| Repeated RLS patterns | Consistent policy templates |
| Common query patterns | Postgres Views or Functions |
| Validation logic | DB check constraints + RPC |
| Audit logging | DB trigger |
| Workspace membership check | Consistent subquery pattern |
Reusable RPC Pattern
If the same business operation happens in multiple places (e.g., "create project + create default task + create activity log"), put it in one RPC function — not in three separate frontend calls.
Bad:
// Frontend makes 3 sequential calls
await supabase.from('projects').insert(...)
await supabase.from('tasks').insert(...)
await supabase.from('activity_logs').insert(...)
Good:
// Frontend makes 1 call, backend handles the rest
await supabase.rpc('create_project_with_defaults', { p_workspace_id, p_name })
4. Reusability Naming Conventions
Names must be clear enough that another team member knows what it does without reading the code.
Functions
verb_noun or getVerb (for getters)
| Good | Bad |
|---|---|
formatDate(date) |
dateHelper(d) |
generateSlug(name) |
makeSlug(n) |
validatePhoneNumber(phone) |
checkPhone(p) |
calculateInvoiceTotal(items) |
doCalc(stuff) |
sendEmailNotification(user, template) |
sendEmail(u, t) |
Components (React)
PascalCase, named by what it IS
| Good | Bad |
|---|---|
ProjectCard |
Card2 |
DeleteConfirmModal |
Popup |
MemberInviteForm |
FormComponent |
WorkspaceSelector |
Dropdown |
Hooks (React)
useNoun or useVerbNoun
| Good | Bad |
|---|---|
useProjects |
useData |
useWorkspaceMembers |
useMembers2 |
useProjectForm |
useForm |
Services (React/Django)
noun.service.ts / NounService
| Good | Bad |
|---|---|
projects.service.ts |
api.ts |
ProjectService |
Helper |
5. Reusability Anti-Patterns
These are the most common violations. Watch for all of them.
| Anti-Pattern | Impact | Fix |
|---|---|---|
| Same component markup in 5 files | One change breaks inconsistently | Extract to shared component |
| Add form and Edit form have different validation | Users can bypass validation on edit | Shared validation schema |
Same fetch call to /projects in 8 components |
Changes require editing 8 files | Shared service/hook |
| Business logic inline in a view function | Cannot reuse, cannot test, grows forever | Extract to service |
| Same 10-line error handler in every form | One fix needed everywhere | Shared handleApiError() util |
| Template section copy-pasted to 4 pages | Update one, forget the rest | {% include %} component |
| Same Xano permission check steps in 12 endpoints | Change once, must update 12 | Xano reusable function |
| WeWeb header rebuilt on every page | Style changes require page-by-page updates | Reusable Element |
Django utility function defined in views.py |
Cannot be used by other views/apps | Move to utils.py |
Hardcoded string "admin" in 20 places |
Rename role → 20 places to update | Constants file |
6. When NOT to Over-Abstract
Reusability is good — over-abstraction creates complexity.
Do not make something reusable if:
× It is only used in one place and has no clear future use.
× Making it "generic" requires more complex parameters than just duplicating it.
× The abstraction would make the code harder to read than the original.
× It is a one-time migration script or throwaway utility.
The rule: Three or more identical uses = extract. One use = keep it where it is.