Development Philosophy
At CaterCow, we adhere to a set of development principles that guide our engineering decisions and ensure we build robust, scalable, and maintainable applications. While some of these concepts may seem fundamental, we believe in their explicit documentation to maintain a consistent and high-quality development standard.
Backend as the Single Source of Truth
The backend is the ultimate authority on the state of the application. The front-end's role is to present this data in a logical and reactive manner. It should not attempt to maintain its own independent state. By leveraging features such as Vue's computed properties for memoization and dependency tracking, this ensures that the UI always reflects the true state of the data as provided by the backend, minimizing inconsistencies and bugs.
The Front-End as an API Consumer
We treat our front-end application as just another consumer of our API, no different from a third-party integration. This philosophy forces us to design our APIs to be intuitive, well-documented, and easy to use. A clean and understandable API benefits not only our front-end developers but also any future integrations or services that may need to interact with our platform.
Backend-Driven Heavy Lifting
The majority of complex business logic and data manipulation should reside on the backend. For any given operation, the front-end should provide sensible and minimal inputs. In return, the API should provide a comprehensive response that includes all the necessary information for the UI to confirm the success and update its display accordingly. This approach simplifies the front-end code, reduces the potential for client-side errors, and centralizes critical logic where it can be more effectively managed and tested.
Embrace Database Transactions
Database transactions are a cornerstone of our data integrity strategy. We use them frequently for operations that involve multiple steps. If an operation is interrupted or fails for any reason, the database automatically reverts to its original state. This atomicity guarantees that our data remains consistent without requiring complex and error-prone manual cleanup code.
Define Comprehensive Rollback Operations
For critical operations, we define explicit rollback procedures. These can be triggered automatically in response to failures or manually by administrators when necessary. This provides a safety net and a clear path to recovery, minimizing downtime and the impact of unforeseen issues.
Principled API Endpoint Security
Our security model is based on a clear distinction between administrative and consumer-facing endpoints:
- Admin CRUD Endpoints: Parameters are blocklisted by default. Field access is granted unless explicitly denied, assuming the user has admin access.
- Consumer Endpoints: Parameters are allowlisted. Fields must be explicitly approved in each endpoint.
This dual approach ensures a secure-by-default posture, protecting sensitive data and functionality.
Acceptance of Eventual Consistency
For many use cases, we accept and embrace eventual consistency. When immediate consistency would require complex and performance-intensive callbacks that heavily load the database, we opt for "syncing" patterns. These patterns update data in the background, ensuring that the system eventually reaches a consistent state without sacrificing performance and user experience for non-critical data.
Leverage the Database for Batch Processing
When it comes to batch processing and large-scale data manipulation, the database is often the right tool for the job. In these scenarios, we are not hesitant to move beyond the limitations of an ORM and utilize raw SQL. This allows us to perform highly efficient operations, such as the "syncing" patterns mentioned previously, by leveraging the full power and optimization of the database engine.
Symmetric API Request and Response Schemas
Whenever practical, our API response schemas should mirror the structure of the corresponding request schemas. This symmetry allows the front-end to seamlessly work with the data. For instance, an editable object fetched from the API can be modified by the user and sent back in the same format to be updated. Assuming the submitted fields are permissible, this creates a straightforward and efficient state synchronization process.
Do Not Trust the Front-End
A fundamental security principle is to never trust data coming from the front-end. The backend must always validate and sanitize all incoming data. The omission of a field in a front-end UI does not mean that field will never show up in a request.
Prioritize Semantic Markup
We are committed to building accessible and user-friendly interfaces. This starts with using semantic HTML markup wherever possible. We avoid creating interactive elements from generic <div> tags and instead opt for <button> and <a> tags for their intended purposes. This provides built-in accessibility features that work reliably across all browsers and assistive technologies, ensuring a better experience for all users.