Let's talk about software development prioritization as well as the development cycle.
Prioritization
When developing a software system, should we choose to build it in a bottom-up fashion, starting from low-level components, or should we follow a top-down style, starting from high-level features? Here is the comparison.
Comparison
- Bottom-up
- Focus on low-level components.
- Steps
- Implement low-level components.
- Assemble low-level components to build high-level features and then the entire system.
- Pros
- Higher modularity encourages code reuse.
- Testing and debugging are easier.
- Cons
- Spending time building components that turn out to be unnecessary or misaligned with the system goal.
- Integration complexity might arise if components have mismatched interfaces or assumptions.
- Use cases
- Reusable libraries or tools that will be integrated into multiple projects.
- Research-heavy projects where the feasibility of fundamental algorithms must be tested early.
- The system requires robust, well-tested components that will serve as the backbone of the system.
- Top-down
- Focus on high-level features.
- Steps
- Define system goal, user experience, high-level features, and system architecture.
- Implement high-level features with fake low-level components.
- Implement low-level components.
- Pros
- Focus on the big picture, ensuring all components align with the system goal.
- Faster prototyping.
- Cons
- Risk of over-engineering.
- Testing of high-level features relies on assumption rather than the actual behavior of low-level components.
- Debugging of high-level feature is hard because the system is more complex.
- Issues of low-level component surface at the end of development. If a low-level component fails to satisfy the predefined requirements, it is cumbersome to modify the high-level design.
- Use cases
- The system has a well-defined set of requirements and a clear vision.
- Projects that need rapid prototypes to validate ideas or get early user feedback.
- System development involves multiple people and high-level design can guide the distribution of tasks.
- Hybrid
- Focus on balancing between high-level features and low-level components.
- Steps
- Define system goal, user experience, high-level features, and system architecture.
- Implement low-level components.
- Assemble low-level components to build high-level features and then the entire system.
- Pros
- Higher modularity encourages code reuse.
- Testing and debugging are easier.
- Focus on the big picture, ensuring all components align with the system goal.
- Cons
- Integration complexity might arise if components have mismatched interfaces or assumptions.
- Risk of over-engineering.
- Use cases
- Complex systems requiring quick feedback.
No matter which way of prioritization we choose, here are some tips:
- Implement things when we actually need them, never when we just foresee that we will need them. It is hard for less experienced developers to appreciate how rarely development for future requirements turns out net-positive.
Now we have figured out our priorities, and we are ready to talk about the development cycle.
Development cycle
- Create user stories based on requirements (plan).
- Pick up user stories from the backlog based on prioritization and put them into a sprint of a two-week development cycle.
- Commit source code into the code repository (code).
- Prefer many small Pull Requests (PR) over one large PR.
- Choose among feature addition, bug fix, and refactoring but not two or more.
- Colleagues are more willing to review many small PRs rather than one giant PR.
- Get feedback early to revise architecture design before spending too much time and energy on less impactful things.
- Smaller PR works well with Continuous Integration / Continuous Delivery (CI/CD) and testing.
- Prefer many small Pull Requests (PR) over one large PR.
- Build artifacts (build).
- Conduct unit tests (test).
- If the build passes the unit test verification, store the artifacts into the artifactory and deploy the build into the development environment, otherwise, go back to step 3.
- Deploy build into the Quality Assurance (QA) environment.
- Conduct QA testing including version-specific test, regression test, cross-version test and performance test.
- If the build passes the QA test verification, deploy the build into the UAT environment, otherwise, go back to step 3.
- Conduct User Acceptance Testing (UAT).
- If the build passes the UAT verification, make it a release candidate and deploy the build into the production environment (deploy).
- Git branch management
- Semantic versioning
- Release schedule
- End-of-life schedule
- Monitor the production environment and watch for alerts. Escalate issues if needed (monitor).
- Error logging
- Performance monitoring
- User feedback collection
- Write documentation (document).
- Versioned documentation
- Migration guide