03 - Software Design
Class: CSCE-331
Notes:
5.1 Design Challenges
Definition of Software Design
- Role: Design is the activity that links requirements to coding and debugging.
- Definition: It is the conception, invention, or contrivance of a scheme for turning a specification into operational software.
- Ideally vs. Reality: While textbooks often portray design as a rational, error-free process derived directly from requirements, the reality is that no system is ever developed in such a clean, linear way.
-
Design Is a Wicked Problem
- Definition: A "wicked" problem is a complex, persistent issue with incomplete, contradictory, and evolving requirements, making it difficult or impossible to solve definitively.
- The Paradox: You essentially have to solve the problem once to define it, and then solve it again to create a solution that actually works.
- Example (Tacoma Narrows Bridge): The original bridge design was a wicked problem because engineers did not know that aerodynamics needed to be a main consideration until the bridge collapsed. Only by building the bridge (solving the problem) could they learn the requirements necessary to build a bridge that would stand,.
- School vs. Professional Work: School assignments are rarely wicked; they are designed to move in a beeline from start to finish. Professional programming problems are wicked because the requirements and constraints often change as the solution emerges.
-
Design Is a Sloppy Process
- Process vs. Result: While the finished design should look tidy and well-organized, the actual process of creating it is messy.
- Role of Mistakes: The point of design is to make mistakes. It is cheaper to make and correct errors during the design phase than to recognize and fix them after coding.
- Completion: It is difficult to know when a design is "good enough" or "done." Because design is open-ended, the most common criterion for stopping is simply "When you're out of time".
-
Design Is About Tradeoffs and Priorities
- Balancing Act: In the real world, designers must weigh competing characteristics. A designer must strike a balance between mutually exclusive goals, such as fast response rates versus minimized development time.
- Prioritization: A good designer chooses a design based on the specific priorities of the project.
-
Design Involves Restrictions
- Purpose: The point of design is partly to create possibilities and partly to restrict them.
- Constraint as a Tool: If resources were infinite, solutions would be sprawling and inefficient. Constraints force simplifications that ultimately improve the solution.
-
Design Is Nondeterministic
- Multiple Solutions: Design is not a process where A + B always equals C. If you send three people to design the same program, they can return with three vastly different, yet essentially acceptable, designs.
- Variety: There are usually dozens of ways to design a computer program.
-
Design Is a Heuristic Process
- Heuristics vs. Algorithms: Design techniques are heuristics ("rules of thumb" or "things to try") rather than algorithms (repeatable instructions guaranteed to produce a specific result).
- Trial and Error: Design involves trial and error. A specific tool or technique that worked on one project may not work on the next.
-
Design Is Emergent
- Evolution: Designs do not spring fully formed from a developer's brain. They evolve and improve through reviews, discussions, and the experience of writing and revising the code itself.
- Change: Virtually all systems undergo design changes during initial development and continue to change as they are extended.
5.2 Key Design Concepts
Software’s Primary Technical Imperative: Managing Complexity
- The Root of Difficulty: The primary reason software development is difficult is complexity. "No one's skull is really big enough to contain a modern computer program".
- Essential vs. Accidental Difficulties: Drawing from Fred Brooks's "No Silver Bullets," difficulties are classified as:
- Essential: Problems inherent to the nature of the software (e.g., the complex logic of the business domain).
- Accidental: Problems related to the production (e.g., clumsy language syntax, noninteractive computers). While accidental difficulties have diminished over time due to better tools and languages, essential difficulties remain,.
- The Goal: The "Primary Technical Imperative" of software design is managing complexity. This involves two approaches:
- Minimizing Essential Complexity: Structure the system so the brain only has to deal with a small amount of complexity at any one time (mental juggling).
- Preventing Accidental Complexity: Keep unnecessary complexity from proliferating.
Desirable Characteristics of a Design A high-quality design strives for these internal characteristics:
- Minimal Complexity: Avoid "clever" designs. If a design doesn't allow you to safely ignore other parts of the program when working on a specific section, it has failed.
- Ease of Maintenance: Design for the maintenance programmer. The code should be self-explanatory.
- Loose Coupling: Minimize connections between different parts of the program. Use encapsulation and abstraction to reduce interconnectedness.
- Extensibility: You should be able to enhance the system without causing violence to the underlying structure. Changes should cause minimal trauma.
- Reusability: Design pieces of the system so they can be reused in other systems.
- High Fan-In: A high number of classes should use a given class. This implies the class is a useful utility.
- Low-to-Medium Fan-Out: A given class should use a low-to-medium number of other classes (more than 7 is often a warning sign).
- Portability: Design the system so it is easy to move to another environment.
- Leanness: The system should have no extra parts. "A book is finished not when nothing more can be added but when nothing more can be taken away".
- Stratification: Keep levels of decomposition distinct. You should be able to view the system at one level without dipping into other levels (e.g., an interface layer to legacy code).
- Standard Techniques: Use standardized, common approaches (like design patterns) rather than exotic ones to give the system a familiar feeling.
Levels of Design
Levels of Design Design occurs at five distinct levels of detail:
Level 1: Software System
- This is the entire system. Design at this level is often neglected, with programmers jumping straight to classes.
Level 2: Division into Subsystems or Packages
- Partitioning: Identifying major subsystems (e.g., database, user interface, business rules). Crucial for projects longer than a few weeks.
- Communication Rules: Restricting communication between subsystems is vital. If all subsystems talk to each other, entropy increases.
- Acyclic Graph: Subsystem relationships should not contain circular dependencies (A uses B, B uses C, C uses A).
- Common Subsystems:
- Business Rules: Laws, regulations, and policies encoded into the system.
- User Interface: Should be isolated so it can evolve without damaging the rest of the program.
- Database Access: Hide implementation details of data access so the rest of the program deals with business-level data abstractions.
- System Dependencies: Package OS or hardware dependencies into a subsystem to facilitate portability.
Level 3: Division into Classes
- This level decomposes subsystems into specific classes.
- Interface Definition: Details of interaction are specified. This level ensures subsystems are detailed enough to be implemented.
- Classes vs. Objects: A class is the static definition (the cookie cutter); an object is the dynamic instance at run time (the cookie).
Level 4: Division into Routines
- This level divides classes into routines (private and public). Designing this level often provides feedback that leads to changes in the class interface (Level 3).
Level 5: Internal Routine Design
- This is the design of the detailed logic within individual routines. It involves writing pseudocode, selecting algorithms, and organizing paragraphs of code.
Here are the comprehensive notes for Chapter 5.4: Design Practices, formatted for your digital notebook.
5.4 Design Practices
Iterate
- The Process: Design is rarely a linear process from point A to point B. It is iterative; you go from A to B and back to A.
- Benefits: The second attempt is nearly always better than the first. Solving a problem one way provides insights that make the second approach superior.
- High and Low Levels: Iteration allows you to switch between high-level (big picture) and low-level (details) views. The big picture provides perspective for details, while details provide a solid foundation for high-level decisions.
- When to Stop: Do not stop at the first design that seems "good enough." You don’t have to solve the whole problem at once; if you get stuck, leave issues unresolved until you have more information,.
Divide and Conquer
- Manage Complexity: Since no one's skull is big enough to contain all details of a complex program, divide the program into different areas of concern.
- Incremental Refinement: Tackle areas individually. If you hit a dead end, iterate. Understand the problem, devise a plan, carry it out, and look back
Top-Down vs. Bottom-Up Design
- Top-Down (Decomposition):
- Concept: Starts with high-level abstractions/generalities and breaks them into manageable pieces/specifics,.
- Pros: Easy for human brains to handle (decomposition). Allows deferring construction details (e.g., file structures) until later,.
- Cons: Can hide low-level complexity that eventually ripples back to the top, complicating the design. Pure top-down is hard to implement because it requires implementing tricky system interfaces last,.
- Bottom-Up (Composition):
- Concept: Starts with specifics/concrete objects and builds up to generalities,.
- Pros: Typically results in early identification of utility functionality, leading to compact designs. Good when low-level constraints (like hardware interfaces) dictate the design,.
- Cons: Hard to get started; sometimes difficult to identify general design principles until details are worked out.
- Conclusion: These are mutually beneficial, not competing. Top-down tends to start simple but may reveal complexity later. Bottom-up starts complex but leads to better high-level factoring. Design is a heuristic process: try both approaches until you find one that works.
Experimental Prototyping
- Purpose: Used to address the "wickedness" of design—when you can't fully define the problem until you've partially solved it (e.g., determining if a database framework meets performance goals),.
- Definition: Writing the absolute minimum amount of throwaway code needed to answer a specific design question.
- Risks:
- Keeping the Code: Developers often fail to treat the code as throwaway, implementing the system instead of prototyping. The prototype code is usually "spaghetti code" and should not be kept.
- Vague Questions: Prototyping works poorly if the design question isn't specific (e.g., "Will this work?" vs. "Will this support 1,000 transactions/sec?").
- Tips: Prototype in a different language (e.g., Python for a Java project) or prefix class names with
prototypeto ensure the code is discarded.
Collaborative Design
- Techniques: Two heads are often better than one. Collaboration ranges from informal discussions and whiteboard sessions to pair programming and formal inspections-.
- Goal Dependency: If the goal is quality assurance, use formal inspections. If the goal is creativity and generating alternatives, use less structured approaches like whiteboard discussions.
How Much Design Is Enough?
- Not an Exact Science: The amount of design needed depends on the project context.
- Factors Influencing Detail and Formality:
- Experience: Inexperienced teams or teams working in unfamiliar domains need medium-to-high detail.
- Risk: Safety-critical or mission-critical applications require high detail and formality.
- Lifetime: Software with a long expected lifetime requires medium detail/formality. Short-term software requires low detail.
- Size: Large projects require more formal design than small projects-.
- General Rule: Err on the side of more detail. The biggest design errors usually arise from areas developers thought were "easy" and therefore didn't design, rather than areas they knew were difficult.
- Documentation vs. Design: Do not confuse the act of designing with documenting the design. Ideally, spend 80% of effort exploring alternatives and 20% documenting. Do not spend 80% polishing documentation of a mediocre design.
Capturing Design Work Formal design documents are not the only way to capture a design. Alternatives include:
- In-Code Documentation: Write design decisions in file or class headers (compatible with tools like Javadoc).
- Wikis: Capture discussions and decisions on a project Wiki for easy editing and linking.
- E-mail Summaries: Designate someone to summarize discussions and archive them.
- Visuals: Use digital cameras to photograph whiteboard drawings or archive flip charts.
- CRC Cards: Use index cards (Class, Responsibility, Collaborator) to design interacting classes, then save the cards.
- UML Diagrams: Use Unified Modeling Language sketches at appropriate levels of detail.
Have you had any experience in robotics? If so, please let us know the following information:
- What was your previous team number?
- How many years of competitive robotics have you participated in?
- What league(s) did you compete in?
- What aspects of competitive robotics would you say you're more experienced in, and why? (CAD, Software, Mechanical, ETC)
- Do you have any technical writing experience (i.e. writing the engineering notebook for your team)?
- Any other information that you have that you would like to let us know about?
Yes, I have prior experience in competitive robotics through FIRST Robotics Competition (FRC). My previous team was XRAMS 6200 from Ciudad Juárez, Mexico. I participated in the 2023 FRC season (Charged Up) as a team member, and continued with the team in the 2024 and 2025 seasons as a mentor.
My primary area of experience is Software. During my time as a student, I worked closely with the lead programmer and was responsible for committing and maintaining the robot code as the robot evolved throughout the season. As a mentor, I have continued to focus on software development and best practices, helping guide students in version control, code organization, and debugging.
I do have technical writing experience. As a mentor, I wrote internal documentation for my team covering programming resources, GitHub workflows, and general coding practices, and I also gave practical presentations to help train newer students.
For the text response, please write about a time that you faced hardship during robotics and how you dealt with it.
One time I faced real hardship during robotics was during a competition match when our robot fell completely and one of the motors that lifted our arm broke on impact. That arm was essential for grabbing and scoring game pieces, so without it we basically could not score any cones anymore. We immediately went into problem-solving mode, but we realized we did not have a backup motor of that exact model. A few teammates and I went scouting through the pits, explaining our situation, and fortunately another team was kind enough to lend us a similar motor that fit our framework design, even though it was not exactly the same one.
Because the motor was different, its behavior was not identical to what our software was tuned for. As a software team member, I had to quickly adjust and test speed and control values between matches through trial and error until we found a configuration that worked well for both autonomous and teleop. In the end, it worked surprisingly well. That experience taught me the importance of staying calm under pressure, asking for help when needed, and adapting quickly with both hardware and software when things do not go as planned.
If you had any past software experiences with a robotics team, please explain here.
Yes, I implemented github repositories and version control for the robot on the team for the first time! for this I had to do hard sessions of documentation and teaching other members how to use github but it was fine and I feel it is one of the most important things my generation did for the team.
Of course I worked on the robot code a lot, I worked on some of the features of our robot for the 2023 season, which was balancing a platform, I did this implementing a gyroscope and coding some PID movement using the gyroscope kind like as an encoder so that the robot corrected its own weight and was able to balance this way, that was a pretty cool thing. I also worked on autonomous path planning which was basically a lot of trial and error with distances and motor power and control, all of it was fun stuff, but the hardest part was on early season were we had to coordinate a lot with the hardware team to get some functions to work, but again it was all part of the fun.
If you've done any past projects regarding software, please explain here.
Yes, I had significant software experience with my robotics team. One of my main contributions was introducing GitHub repositories and version control for the robot code for the first time in the team's history. This required writing documentation and running several training sessions to teach other members how to use GitHub properly. It took effort, but I believe it was one of the most impactful improvements my generation made for the team.
I also worked extensively on the robot code itself. For the 2023 season, I helped implement the balancing feature by integrating a gyroscope and coding PID-based control so the robot could correct its own position and balance on the platform. I treated the gyroscope readings almost like a motor encoder to keep the robot stable, which was a really rewarding challenge.
In addition, I worked on autonomous path planning, which involved a lot of trial and error with distances, motor power, and control tuning. Early in the season, one of the hardest parts was coordinating closely with the mechanical team so that software and hardware matched well, but that collaboration ended up being one of the most fun and valuable parts of the experience.
If you've done any past projects regarding software, please explain here.
Yes, I have worked on several software projects as I have progressed through my degree. Many of my classes have required building fairly large projects, which has helped me learn solid software engineering practices such as code organization, debugging, and basic design principles.
Outside of class, a big part of my project experience comes from hackathons. Through those, I have worked on a variety of projects, including web applications, machine learning models, Java GUI applications, simple CLI tools in C, and small games in Python. Hackathons have been where I get to experiment the most, learn quickly, and work closely with teammates under time pressure.
In addition to these projects, I also gained a lot of practical experience through robotics, especially writing and maintaining Java code for our robot. All of these experiences together have helped me become comfortable working with different languages, frameworks, and styles of development.