Best Practices for Designing a Clean and Robust Backend Project
This article shares practical guidelines for new backend projects, covering database design, module organization, scheduled task management, meaningful naming and comments, code robustness, clear error messages, and comprehensive self‑testing to help developers build maintainable and scalable systems.
When starting a new backend project, it is easy to focus only on delivering features and overlook the importance of solid architecture, which can lead to redundant code and maintenance headaches later on.
01. Introduction The author reflects on personal growth from a junior developer to being able to design databases and develop full‑stack features, and outlines the need for a clear development approach.
02. Database Design After setting up the project framework, design the MongoDB schema based on the prototype, keeping in mind:
Minimize the use of arrays for ambiguous data.
Use MongoDB’s default _id as primary key, or other stable identifiers such as email or project ID.
Avoid duplicating one‑to‑one data across multiple collections.
Add constraints for unrealistic values (e.g., likes cannot be negative).
Split large collections (>16 MB) or use GridFS.
Update documentation whenever the schema changes.
03. Module Division and Encapsulation Group backend endpoints by functional domain (e.g., all "daily report" APIs in one module). Extract common logic into decorators or utility functions, such as an auth_required decorator:
def auth_required(auth_list):
def handle_func(func):
@wraps(func)
def wrapper(*args, **kwargs):
email = session.get('email')
# Determine user permissions
if email:
have_auth = check_auth() # permission check
if have_auth:
return func(*args, **kwargs)
return "权限不足", 403
return "login required", 401
return wrapper
return handle_funcPlace reusable utilities (e.g., time formatting) in backend/utils/util.py and be aware of circular imports.
04. Scheduled Task Management Avoid real‑time polling for every request; instead, schedule periodic updates (hourly, daily) for reports or static data. Provide manual refresh buttons for data that truly needs real‑time updates.
05. Meaningful Variable Names and Comments Clear naming and appropriate comments make future maintenance easier, especially when revisiting code after months.
06. Code Robustness Ensure code can handle edge cases:
Use dict.get(key, default) to avoid KeyError when keys may be missing.
# Normal case
student = {"name": "Maria", "age": 18, "score": 80}
name = student["name"]
# When key might be missing
student = {"name": "Maria"}
score = student.get("score", 0) # safe fallbackCheck for None before accessing database results.
tmp = mongo.db.students.find_one({"name": "Jack"})
if tmp is not None:
# proceed with processing
pass
else:
logger.error("Student not found")
return "", 400Wrap risky operations in try/except and log detailed tracebacks:
import traceback
try:
# risky code
pass
except Exception:
msg = f"[function name] error msg\n{traceback.format_exc()}"
logger.error(msg)
return "xxx出现错误,已自动通知开发者(具体是谁),修复后会及时告知", 40407. Clear Error Messages Provide specific error feedback to users (e.g., "Server connection failed" or "Image must be PNG") instead of generic messages.
08. Comprehensive Self‑Testing Developers should perform thorough self‑tests before handing over to QA, including:
Simulating user actions rather than directly manipulating the database.
Testing with and without mock data to ensure graceful handling of empty states.
Conducting performance and pagination tests for large data sets.
09. Conclusion These practices, while familiar to seasoned engineers, aim to help newcomers quickly get up to speed and build projects with better maintainability and quality.
NetEase LeiHuo Testing Center
LeiHuo Testing Center provides high-quality, efficient QA services, striving to become a leading testing team in China.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.