This site is not ready yet! The updated version will be available soon.
CS2103/T 2020 Jan-Apr
  • Full Timeline
  • Week 1 [Aug 12]
  • Week 2 [Aug 19]
  • Week 3 [Aug 26]
  • Week 4 [Sep 2]
  • Week 5 [Sep 9]
  • Week 6 [Sep 16]
  • Week 7 [Sep 30]
  • Week 8 [Oct 7]
  • Week 9 [Oct 14]
  • Week 10 [Oct 21]
  • Week 11 [Oct 28]
  • Week 12 [Nov 4]
  • Week 13 [Nov 11]
  • Textbook
  • Admin Info
  • Report Bugs
  • Forum
  • Instructors
  • Announcements
  • File Submissions
  • Tutorial Schedule
  • Java Coding Standard
  • Participation Marks List

  •  Individual Project (iP):
  • Individual Project Info
  • Duke Upstream Repo
  • iP Code Dashboard
  • iP Showcase

  •  Team Project (tP):
  • Team Project Info
  • Team IDs
  • Addressbook-level3
  • Addressbook-level 1,2,4
  • tP Code Dashboard
  • tP Showcase
  • Week 5 [Sep 9] - Topics

    • [W5.1] Requirements: Intro
    • [W5.1a] Requirements → Requirements → Introduction

    • [W5.1b] Requirements → Requirements → Non-Functional Requirements

    • [W5.1c] Requirements → Requirements → Quality of Requirements

    • [W5.1d] Requirements → Requirements → Prioritizing Requirements

    • [W5.2] Requirements: Gathering
    • [W5.2a] Requirements → Gathering Requirements → Brainstorming

    • [W5.2b] Requirements → Gathering Requirements → Product Surveys

    • [W5.2c] Requirements → Gathering Requirements → Observation

    • [W5.2d] Requirements → Gathering Requirements → User Surveys

    • [W5.2e] Requirements → Gathering Requirements → Interviews

    • [W5.2f] Requirements → Gathering Requirements → Focus Groups

    • [W5.2g] Requirements → Gathering Requirements → Prototyping

    • [W5.3] Requirements: Specifying

       Prose

    • [W5.3a] Requirements → Specifying Requirements → Prose → What

       Feature Lists

    • [W5.3b] Requirements → Specifying Requirements → Feature Lists → What

       User Stories

    • [W5.3c] Requirements → Specifying Requirements → User Stories → Introduction

    • [W5.3d] Requirements → Specifying Requirements → User Stories → Details

    • [W5.3e] Requirements → Specifying Requirements → User Stories → Usage

       Glossary

    • [W5.3f] Requirements → Specifying Requirements → Glossary → What

       Supplementary Requirements

    • [W5.3g] Requirements → Specifying Requirements → Supplementary Requirements → What

    • [W5.4] Code Quality

       Readability

    • [W5.4a] Implementation → Code Quality → Readability → Introduction

    • [W5.4b] Implementation → Code Quality → Readability → Basic → Avoid Long Methods

    • [W5.4c] Implementation → Code Quality → Readability → Basic → Avoid Deep Nesting

    • [W5.4d] Implementation → Code Quality → Readability → Basic → Avoid Complicated Expressions

    • [W5.4e] Implementation → Code Quality → Readability → Basic → Avoid Magic Numbers

    • [W5.4f] Implementation → Code Quality → Readability → Basic → Make the Code Obvious

    • [W5.4g] Implementation → Code Quality → Readability → Intermediate → Structure Code Logically

    • [W5.4h] Implementation → Code Quality → Readability → Intermediate → Do Not 'Trip Up' Reader

    • [W5.4i] Implementation → Code Quality → Readability → Intermediate → Practice KISSing

    • [W5.4j] Implementation → Code Quality → Readability → Intermediate → Avoid Premature Optimizations

    • [W5.4k] Implementation → Code Quality → Readability → Intermediate → SLAP Hard

    • [W5.4l] Implementation → Code Quality → Readability → Advanced → Make the Happy Path Prominent

       Naming

    • [W5.4m] Implementation → Code Quality → Naming → Introduction

    • [W5.4n] Implementation → Code Quality → Naming → Basic → Use Nouns for Things and Verbs for Actions

    • [W5.4o] Implementation → Code Quality → Naming → Basic → Use Standard Words

    • [W5.4p] Implementation → Code Quality → Naming → Intermediate → Use Name to Explain

    • [W5.4q] Implementation → Code Quality → Naming → Intermediate → Not Too Long, Not Too Short

    • [W5.4r] Implementation → Code Quality → Naming → Intermediate → Avoid Misleading Names

       Unsafe Practices

    • [W5.4s] Implementation → Code Quality → Error-Prone Practices → Introduction

    • [W5.4t] Implementation → Code Quality → Error-Prone Practices → Basic → Use the Default Branch

    • [W5.4u] Implementation → Code Quality → Error-Prone Practices → Basic → Don't Recycle Variables or Parameters

    • [W5.4v] Implementation → Code Quality → Error-Prone Practices → Basic → Avoid Empty Catch Blocks

    • [W5.4w] Implementation → Code Quality → Error-Prone Practices → Basic → Delete Dead Code

    • [W5.4x] Implementation → Code Quality → Error-Prone Practices → Intermediate → Minimize Scope of Variables

    • [W5.4y] Implementation → Code Quality → Error-Prone Practices → Intermediate → Minimize Code Duplication

       Code Comments

    • [W5.4z] Implementation → Code Quality → Comments → Introduction

    • [W5.4A] Implementation → Code Quality → Comments → Basic → Do Not Repeat the Obvious

    • [W5.4B] Implementation → Code Quality → Comments → Basic → Write to the Reader

    • [W5.4C] Implementation → Code Quality → Comments → Intermediate → Explain WHAT and WHY, not HOW

    • [W5.5] Refactoring
    • [W5.5a] Implementation → Refactoring → What

    • [W5.5b] Tools → Intellij IDEA → Refactoring

    • [W5.5c] Implementation → Refactoring → How

    • [W5.5d] Implementation → Refactoring → When

    • [W5.6a] Implementation → Error Handling → Assertions → What :

    • [W5.6b] Implementation → Error Handling → Assertions → How :

    • [W5.6c] Implementation → Error Handling → Assertions → When :

    • [W5.7] Java: streams
    • [W5.7a] Tools → Java → Streams: Basic :

    • [W5.8] Continuous Integration/Deployment
    • [W5.8a] Implementation → Integration → Introduction → What

    • [W5.8b] Implementation → Integration → Build Automation → What

    • [W5.8c] Implementation → Integration → Build Automation → Continuous Integration and Continuous Deployment


    [W5.1] Requirements: Intro

    W5.1a

    Requirements → Requirements → Introduction

    Can explain requirements

    A software requirement specifies a need to be fulfilled by the software product.

    A software project may be,

    • a brown-field project i.e., develop a product to replace/update an existing software product
    • a green-field project i.e., develop a totally new system with no precedent

    In either case, requirements need to be gathered, analyzed, specified, and managed.

    Requirements come from stakeholders.

    Stakeholder: A party that is potentially affected by the software project. e.g. users, sponsors, developers, interest groups, government agencies, etc.

    Identifying requirements is often not easy. For example, stakeholders may not be aware of their precise needs, may not know how to communicate their requirements correctly, may not be willing to spend effort in identifying requirements, etc.

    W5.1b

    Requirements → Requirements → Non-Functional Requirements

    Can explain non-functional requirements

    Requirements can be divided into two in the following way:

    1. Functional requirements specify what the system should do.
    2. Non-functional requirements specify the constraints under which system is developed and operated.

    Some examples of non-functional requirement categories:

    • Data requirements e.g. size, volatility, persistency etc.,
    • Environment requirements e.g. technical environment in which system would operate or need to be compatible with.
    • Accessibility, Capacity, Compliance with regulations, Documentation, Disaster recovery, Efficiency, Extensibility, Fault tolerance, Interoperability, Maintainability, Privacy, Portability, Quality, Reliability, Response time, Robustness, Scalability, Security, Stability, Testability, and more ...
    • Business/domain rules: e.g. the size of the minefield cannot be smaller than five.
    • Constraints: e.g. the system should be backward compatible with data produced by earlier versions of the system; system testers are available only during the last month of the project; the total project cost should not exceed $1.5 million.
    • Technical requirements: e.g. the system should work on both 32-bit and 64-bit environments.
    • Performance requirements: e.g. the system should respond within two seconds.
    • Quality requirements: e.g. the system should be usable by a novice who has never carried out an online purchase.
    • Process requirements: e.g. the project is expected to adhere to a schedule that delivers a feature set every one month.
    • Notes about project scope: e.g. the product is not required to handle the printing of reports.
    • Any other noteworthy points: e.g. the game should not use images deemed offensive to those injured in real mine clearing activities.

    We may have to spend an extra effort in digging NFRs out as early as possible because,

    1. NFRs are easier to miss e.g., stakeholders tend to think of functional requirements first
    2. sometimes NFRs are critical to the success of the software. E.g. A web application that is too slow or that has low security is unlikely to succeed even if it has all the right functionality.

    Given below are some requirements of TEAMMATES (an online peer evaluation system for education). Which one of these are non-functional requirements?

    • a. The response to any use action should become visible within 5 seconds.
    • b. The application admin should be able to view a log of user activities.
    • c. The source code should be open source.
    • d. A course should be able to have up to 2000 students.
    • e. As a student user, I can view details of my team members so that I can know who they are.
    • f. The user interface should be intuitive enough for users who are not IT-savvy.
    • g. The product is offered as a free online service.

    (a)(c)(d)(f)(g)

    Explanation: (b) are (e) are functions available for a specific user types. Therefore, they are functional requirements. (a), (c), (d), (f) and (g) are either constraints on functionality or constraints on how the project is done, both of which are considered non-functional requirements.

    W5.1c

    Requirements → Requirements → Quality of Requirements

    Can explain quality of requirements

    Here are some characteristics of well-defined requirements [📖 zielczynski]:

    • Unambiguous
    • Testable (verifiable)
    • Clear (concise, terse, simple, precise)
    • Correct
    • Understandable
    • Feasible (realistic, possible)
    • Independent
    • Atomic
    • Necessary
    • Implementation-free (i.e. abstract)

    Besides these criteria for individual requirements, the set of requirements as a whole should be

    • Consistent
    • Non-redundant
    • Complete

    Peter Zielczynski, Requirements Management Using IBM Rational RequisitePro, IBM Press, 2008

    W5.1d

    Requirements → Requirements → Prioritizing Requirements

    Can explain prioritizing requirements

    Requirements can be prioritized based the importance and urgency, while keeping in mind the constraints of schedule, budget, staff resources, quality goals, and other constraints.

    A common approach is to group requirements into priority categories. Note that all such scales are subjective, and stakeholders define the meaning of each level in the scale for the project at hand.

    An example scheme for categorizing requirements:

    • Essential: The product must have this requirement fulfilled or else it does not get user acceptance
    • Typical: Most similar systems have this feature although the product can survive without it.
    • Novel: New features that could differentiate this product from the rest.

    Other schemes:

    • High, Medium, Low
    • Must-have, Nice-to-have, Unlikely-to-have
    • Level 0, Level 1, Level 2, ...

    Some requirements can be discarded if they are considered ‘out of scope’.

    The requirement given below is for a Calendar application. Stakeholder of the software (e.g. product designers) might decide the following requirement is not in the scope of the software.

    The software records the actual time taken by each task and show the difference between the actual and scheduled time for the task.

    [W5.2] Requirements: Gathering

    W5.2a

    Requirements → Gathering Requirements → Brainstorming

    Can explain brainstorming

    Brainstorming: A group activity designed to generate a large number of diverse and creative ideas for the solution of a problem.

    In a brainstorming session there are no "bad" ideas. The aim is to generate ideas; not to validate them. Brainstorming encourages you to "think outside the box" and put "crazy" ideas on the table without fear of rejection.

    What is the key characteristic about brainstorming?

    (b)

    W5.2b

    Requirements → Gathering Requirements → Product Surveys

    Can explain product surveys

    Studying existing products can unearth shortcomings of existing solutions that can be addressed by a new product. Product manuals and other forms of documentation of an existing system can tell us how the existing solutions work.

    When developing a game for a mobile device, a look at a similar PC game can give insight into the kind of features and interactions the mobile game can offer.

    W5.2c

    Requirements → Gathering Requirements → Observation

    Can explain observation

    Observing users in their natural work environment can uncover product requirements. Usage data of an existing system can also be used to gather information about how an existing system is being used, which can help in building a better replacement e.g. to find the situations where the user makes mistakes when using the current system.

    W5.2d

    Requirements → Gathering Requirements → User Surveys

    Can explain user surveys

    Surveys can be used to solicit responses and opinions from a large number of stakeholders regarding a current product or a new product.

    W5.2e

    Requirements → Gathering Requirements → Interviews

    Can explain interviews

    Interviewing stakeholders and domain experts can produce useful information that project requirements.

    Domain Expert : An expert of a discipline to which the product is connected e.g., for a software used for Accounting, a domain expert is someone who is an expert of Accounting.

    W5.2f

    Requirements → Gathering Requirements → Focus Groups

    Can explain focus groups


    [source]

    Focus groups are a kind of informal interview within an interactive group setting. A group of people (e.g. potential users, beta testers) are asked about their understanding of a specific issue, process, product, advertisement, etc.

    : How do focus groups work? - Hector Lanz extra

    W5.2g

    Requirements → Gathering Requirements → Prototyping

    Can explain prototyping

    Prototype: A prototype is a mock up, a scaled down version, or a partial system constructed

    • to get users’ feedback.
    • to validate a technical concept (a "proof-of-concept" prototype).
    • to give a preview of what is to come, or to compare multiple alternatives on a small scale before committing fully to one alternative.
    • for early field-testing under controlled conditions.

    Prototyping can uncover requirements, in particular, those related to how users interact with the system. UI prototypes or mock ups are often used in brainstorming sessions, or in meetings with the users to get quick feedback from them.

    A mock up (also called a wireframe diagram) of a dialog box:


    [source: plantuml.com]

    Prototyping can be used for discovering as well as specifying requirements e.g. a UI prototype can serve as a specification of what to build.

    [W5.3] Requirements: Specifying


    Prose

    W5.3a

    Requirements → Specifying Requirements → Prose → What

    Can explain prose

    A textual description (i.e. prose) can be used to describe requirements. Prose is especially useful when describing abstract ideas such as the vision of a product.

    The product vision of the TEAMMATES Project given below is described using prose.

    TEAMMATES aims to become the biggest student project in the world (biggest here refers to 'many contributors, many users, large code base, evolving over a long period'). Furthermore, it aims to serve as a training tool for Software Engineering students who want to learn SE skills in the context of a non-trivial real software product.

    Avoid using lengthy prose to describe requirements; they can be hard to follow.


    Feature Lists

    W5.3b

    Requirements → Specifying Requirements → Feature Lists → What

    Can explain feature list

    Feature List: A list of features of a product grouped according to some criteria such as aspect, priority, order of delivery, etc.

    A sample feature list from a simple Minesweeper game (only a brief description has been provided to save space):

    1. Basic play – Single player play.
    2. Difficulty levels
      • Medium-levels
      • Advanced levels
    3. Versus play – Two players can play against each other.
    4. Timer – Additional fixed time restriction on the player.
    5. ...

    User Stories

    W5.3c

    Requirements → Specifying Requirements → User Stories → Introduction

    Can write simple user stories

    User story: User stories are short, simple descriptions of a feature told from the perspective of the person who desires the new capability, usually a user or customer of the system. [Mike Cohn]

    A common format for writing user stories is:

    User story format: As a {user type/role} I can {function} so that {benefit}

    Examples (from a Learning Management System):

    1. As a student, I can download files uploaded by lecturers, so that I can get my own copy of the files
    2. As a lecturer, I can create discussion forums, so that students can discuss things online
    3. As a tutor, I can print attendance sheets, so that I can take attendance during the class

    We can write user stories on index cards or sticky notes, and arrange on walls or tables, to facilitate planning and discussion. Alternatively, we can use a software (e.g., GitHub Project Boards, Trello, Google Docs, ...) to manage user stories digitally.

    [credit: https://www.flickr.com/photos/jakuza/2682466984/]

    [credit: https://www.flickr.com/photos/jakuza/with/2726048607/]

    [credit: https://commons.wikimedia.org/wiki/File:User_Story_Map_in_Action.png]

    • a. They are based on stories users tell about similar systems
    • b. They are written from the user/customer perspective
    • c. They are always written in some physical medium such as index cards or sticky notes
    • a. Reason: Despite the name, user stories are not related to 'stories' about the software.
    • b.
    • c. Reason: It is possible to use software to record user stories. When the team members are not co-located this may be the only option.

    Critique the following user story taken from a software project to build an e-commerce website.

    As a developer, I want to use Python to implement the software, so that we can resue existing Python modules.

    Refer to the definition of a user story.

    User story: User stories are short, simple descriptions of a feature told from the perspective of the person who desires the new capability, usually a user or customer of the system. [Mike Cohn]

    This user story is not written from the perspective of the user/customer.

    Bill wants you to build a Human Resource Management (HRM) system. He mentions that the system will help employees to view their own leave balance. What are the user stories you can extract from that statement?

    Remember to follow the correct format when writing user stories.

    User story format: As a {user type/role} I can {function} so that {benefit}

    As an employee, I can view my leave balance, so that I can know how many leave days I have left.

    Note: the {benefit} part may vary as it is not specifically mentioned in the question.

    W5.3d

    Requirements → Specifying Requirements → User Stories → Details

    Can write more detailed user stories

    The {benefit} can be omitted if it is obvious.

    As a user, I can login to the system so that I can access my data

    It is recommended to confirm there is a concrete benefit even if you omit it from the user story. If not, you could end up adding features that have no real benefit.

    You can add more characteristics to the {user role} to provide more context to the user story.

    • As a forgetful user, I can view a password hint, so that I can recall my password.
    • As an expert user, I can tweak the underlying formatting tags of the document, so that I can format the document exactly as I need.

    You can write user stories at various levels. High-level user stories, called epics (or themes) cover bigger functionality. You can then break down these epics to multiple user stories of normal size.

    [Epic] As a lecturer, I can monitor student participation levels

    • As a lecturer, I can view the forum post count of each student
      so that I can identify the activity level of students in the forum
    • As a lecturer, I can view webcast view records of each student
      so that I can identify the students who did not view webcasts
    • As a lecturer, I can view file download statistics of each student
      so that I can identify the students who do not download lecture materials

    You can add conditions of satisfaction to a user story to specify things that need to be true for the user story implementation to be accepted as ‘done’.

    As a lecturer, I can view the forum post count of each student so that I can identify the activity level of students in the forum.

    Conditions:

    Separate post count for each forum should be shown
    Total post count of a student should be shown
    The list should be sortable by student name and post count

    Other useful info that can be added to a user story includes (but not limited to)

    • Priority: how important the user story is
    • Size: the estimated effort to implement the user story
    • Urgency: how soon the feature is needed
    More examples extra

    User stories for a travel website (credit: Mike Cohen)

    • As a registered user, I am required to log in so that I can access the system
    • As a forgetful user, I can request a password reminder so that I can log in if I forget mine
    • [Epic] As a user, I can cancel a reservation
      • As a premium site member, I can cancel a reservation up to the last minute
      • As a non-premium member, I can cancel up to 24 hours in advance
      • As a member, I am emailed a confirmation of any cancelled reservation
    • [Epic] As a frequent flyer, I want to book a trip
      • As a frequent flyer, I want to book a trip using miles
      • As a frequent flyer, I want to rebook a trip I take often
      • As a frequent flyer, I want to request an upgrade
      • As a frequent flyer, I want to see if my upgrade cleared

    Choose the correct statements

    • a. User stories are short and written in a formal notation.
    • b. User stories is another name for use cases.
    • c. User stories describes past experiences users had with similar systems. These are helpful in developing the new system.
    • d. User stories are not detailed enough to tell us exact details of the product.
    • a.
    • b.
    • c.
    • d.

    Explanation: User stories are short and written in natural language, NOT in a formal language. They are used for estimation and scheduling purposes but do not contain enough details to form a complete system specification.

    W5.3e

    Requirements → Specifying Requirements → User Stories → Usage

    Can use user stories to manage requirements of project

    User stories capture user requirements in a way that is convenient for scoping, estimation, and scheduling.

    [User stories] strongly shift the focus from writing about features to discussing them. In fact, these discussions are more important than whatever text is written. [Mike Cohn, MountainGoat Software 🔗]

    User stories differ from traditional requirements specifications mainly in the level of detail. User stories should only provide enough details to make a reasonably low risk estimate of how long the user story will take to implement. When the time comes to implement the user story, the developers will meet with the customer face-to-face to work out a more detailed description of the requirements. [more...]

    User stories can capture non-functional requirements too because even NFRs must benefit some stakeholder.

    Requirements → Requirements →

    Non-Functional Requirements

    Requirements can be divided into two in the following way:

    1. Functional requirements specify what the system should do.
    2. Non-functional requirements specify the constraints under which system is developed and operated.

    Some examples of non-functional requirement categories:

    • Data requirements e.g. size, volatility, persistency etc.,
    • Environment requirements e.g. technical environment in which system would operate or need to be compatible with.
    • Accessibility, Capacity, Compliance with regulations, Documentation, Disaster recovery, Efficiency, Extensibility, Fault tolerance, Interoperability, Maintainability, Privacy, Portability, Quality, Reliability, Response time, Robustness, Scalability, Security, Stability, Testability, and more ...
    • Business/domain rules: e.g. the size of the minefield cannot be smaller than five.
    • Constraints: e.g. the system should be backward compatible with data produced by earlier versions of the system; system testers are available only during the last month of the project; the total project cost should not exceed $1.5 million.
    • Technical requirements: e.g. the system should work on both 32-bit and 64-bit environments.
    • Performance requirements: e.g. the system should respond within two seconds.
    • Quality requirements: e.g. the system should be usable by a novice who has never carried out an online purchase.
    • Process requirements: e.g. the project is expected to adhere to a schedule that delivers a feature set every one month.
    • Notes about project scope: e.g. the product is not required to handle the printing of reports.
    • Any other noteworthy points: e.g. the game should not use images deemed offensive to those injured in real mine clearing activities.

    We may have to spend an extra effort in digging NFRs out as early as possible because,

    1. NFRs are easier to miss e.g., stakeholders tend to think of functional requirements first
    2. sometimes NFRs are critical to the success of the software. E.g. A web application that is too slow or that has low security is unlikely to succeed even if it has all the right functionality.

    Given below are some requirements of TEAMMATES (an online peer evaluation system for education). Which one of these are non-functional requirements?

    • a. The response to any use action should become visible within 5 seconds.
    • b. The application admin should be able to view a log of user activities.
    • c. The source code should be open source.
    • d. A course should be able to have up to 2000 students.
    • e. As a student user, I can view details of my team members so that I can know who they are.
    • f. The user interface should be intuitive enough for users who are not IT-savvy.
    • g. The product is offered as a free online service.

    (a)(c)(d)(f)(g)

    Explanation: (b) are (e) are functions available for a specific user types. Therefore, they are functional requirements. (a), (c), (d), (f) and (g) are either constraints on functionality or constraints on how the project is done, both of which are considered non-functional requirements.

    An example of a NFR captured as a user story:

    As a I want to so that
    impatient user to be able experience reasonable response time from the website while up to 1000 concurrent users are using it I can use the app even when the traffic is at the maximum expected level

    Given their lightweight nature, user stories are quite handy for recording requirements during early stages of requirements gathering.

    Here are some tips for using user stories for early stages of requirement gathering:

    • Define the target user:
      Decide your target user's profile (e.g. a student, office worker, programmer, sales person) and work patterns (e.g. Does he work in groups or alone? Does he share his computer with others?). A clear understanding of the target user will help when deciding the importance of a user story. You can even give this user a name. e.g. Target user Jean is a university student studying in a non-IT field. She interacts with a lot of people due to her involvement in university clubs/societies. ...
    • Define the problem scope: Decide that exact problem you are going to solve for the target user. e.g. Help Jean keep track of all her school contacts
    • Don't be too hasty to discard 'unusual' user stories:
      Those might make your product unique and stand out from the rest, at least for the target users.
    • Don't go into too much details:
      For example, consider this user story: As a user, I want to see a list of tasks that needs my attention most at the present time, so that I pay attention to them first.
      When discussing this user story, don't worry about what tasks should be considered needs my attention most at the present time. Those details can be worked out later.
    • Don't be biased by preconceived product ideas:
      When you are at the stage of identifying user needs, clear your mind of ideas you have about what your end product will look like.
    • Don't discuss implementation details or whether you are actually going to implement it:
      When gathering requirements, your decision is whether the user's need is important enough for you to want to fulfil it. Implementation details can be discussed later. If a user story turns out to be too difficult to implement later, you can always omit it from the implementation plan.

    While use cases can be recorded on physical paper in the initial stages, an online tool is more suitable for longer-term management of user stories, especially if the team is not co-located.

    You can create issues for each of the user stories and use a GitHub Project Board to sort them into categories.

    Example Project Board:

    Example Issue to represent a user story:

    A video on GitHub Project Boards:

    Example Google Sheet for recording user stories:

    Example Trello Board for recording user stories:

    eXtreme Programming (XP)

    Extreme programming (XP) is a software development methodology which is intended to improve software quality and responsiveness to changing customer requirements. As a type of agile software development, it advocates frequent "releases" in short development cycles, which is intended to improve productivity and introduce checkpoints at which new customer requirements can be adopted. [wikipedia, 2017.05.01]

    uses User stories to capture requirements.

    This page in their website explains the difference between user stories and traditional requirements.

    One of the biggest misunderstandings with user stories is how they differ from traditional requirements specifications. The biggest difference is in the level of detail. User stories should only provide enough detail to make a reasonably low risk estimate of how long the story will take to implement. When the time comes to implement the story developers will go to the customer and receive a detailed description of the requirements face to face.


    Glossary

    W5.3f

    Requirements → Specifying Requirements → Glossary → What

    Can explain glossary

    Glossary: A glossary serves to ensure that all stakeholders have a common understanding of the noteworthy terms, abbreviations, acronyms etc.

    Here is a partial glossary from a variant of the Snakes and Ladders game:

    • Conditional square: A square that specifies a specific face value which a player has to throw before his/her piece can leave the square.
    • Normal square: a normal square does not have any conditions, snakes, or ladders in it.

    Supplementary Requirements

    W5.3g

    Requirements → Specifying Requirements → Supplementary Requirements → What

    Can explain supplementary requirements

    A supplementary requirements section can be used to capture requirements that do not fit elsewhere. Typically, this is where most Non Functional Requirements will be listed.

    Requirements → Requirements →

    Non-Functional Requirements

    Requirements can be divided into two in the following way:

    1. Functional requirements specify what the system should do.
    2. Non-functional requirements specify the constraints under which system is developed and operated.

    Some examples of non-functional requirement categories:

    • Data requirements e.g. size, volatility, persistency etc.,
    • Environment requirements e.g. technical environment in which system would operate or need to be compatible with.
    • Accessibility, Capacity, Compliance with regulations, Documentation, Disaster recovery, Efficiency, Extensibility, Fault tolerance, Interoperability, Maintainability, Privacy, Portability, Quality, Reliability, Response time, Robustness, Scalability, Security, Stability, Testability, and more ...
    • Business/domain rules: e.g. the size of the minefield cannot be smaller than five.
    • Constraints: e.g. the system should be backward compatible with data produced by earlier versions of the system; system testers are available only during the last month of the project; the total project cost should not exceed $1.5 million.
    • Technical requirements: e.g. the system should work on both 32-bit and 64-bit environments.
    • Performance requirements: e.g. the system should respond within two seconds.
    • Quality requirements: e.g. the system should be usable by a novice who has never carried out an online purchase.
    • Process requirements: e.g. the project is expected to adhere to a schedule that delivers a feature set every one month.
    • Notes about project scope: e.g. the product is not required to handle the printing of reports.
    • Any other noteworthy points: e.g. the game should not use images deemed offensive to those injured in real mine clearing activities.

    We may have to spend an extra effort in digging NFRs out as early as possible because,

    1. NFRs are easier to miss e.g., stakeholders tend to think of functional requirements first
    2. sometimes NFRs are critical to the success of the software. E.g. A web application that is too slow or that has low security is unlikely to succeed even if it has all the right functionality.

    Given below are some requirements of TEAMMATES (an online peer evaluation system for education). Which one of these are non-functional requirements?

    • a. The response to any use action should become visible within 5 seconds.
    • b. The application admin should be able to view a log of user activities.
    • c. The source code should be open source.
    • d. A course should be able to have up to 2000 students.
    • e. As a student user, I can view details of my team members so that I can know who they are.
    • f. The user interface should be intuitive enough for users who are not IT-savvy.
    • g. The product is offered as a free online service.

    (a)(c)(d)(f)(g)

    Explanation: (b) are (e) are functions available for a specific user types. Therefore, they are functional requirements. (a), (c), (d), (f) and (g) are either constraints on functionality or constraints on how the project is done, both of which are considered non-functional requirements.

    [W5.4] Code Quality


    Readability

    W5.4a

    Implementation → Code Quality → Readability → Introduction

    Can explain the importance of readability

    Programs should be written and polished until they acquire publication quality. --Niklaus Wirth

    Among various dimensions of code quality, such as run-time efficiency, security, and robustness, one of the most important is understandability. This is because in any non-trivial software project, code needs to be read, understood, and modified by other developers later on. Even if we do not intend to pass the code to someone else, code quality is still important because we all become 'strangers' to our own code someday.

    The two code samples given below achieve the same functionality, but one is easier to read.

    Bad

    int subsidy() {
        int subsidy;
        if (!age) {
            if (!sub) {
                if (!notFullTime) {
                    subsidy = 500;
                } else {
                    subsidy = 250;
                }
            } else {
                subsidy = 250;
            }
        } else {
            subsidy = -1;
        }
        return subsidy;
    }
    
      

    Good

    int calculateSubsidy() {
        int subsidy;
        if (isSenior) {
            subsidy = REJECT_SENIOR;
        } else if (isAlreadySubsidised) {
            subsidy = SUBSIDISED_SUBSIDY;
        } else if (isPartTime) {
            subsidy = FULLTIME_SUBSIDY * RATIO;
        } else {
            subsidy = FULLTIME_SUBSIDY;
        }
        return subsidy;
    }
    

    Bad

    def calculate_subs():
        if not age:
            if not sub:
                if not not_fulltime:
                    subsidy = 500
                else:
                    subsidy = 250
            else:
                subsidy = 250
        else:
            subsidy = -1
        return subsidy
    
      

    Good

    def calculate_subsidy():
        if is_senior:
            return REJECT_SENIOR
        elif is_already_subsidised:
            return SUBSIDISED_SUBSIDY
        elif is_parttime:
            return FULLTIME_SUBSIDY * RATIO
        else:
            return FULLTIME_SUBSIDY
    

    W5.4b

    Implementation → Code Quality → Readability → Basic → Avoid Long Methods

    Can improve code quality using technique: avoid long methods

    Be wary when a method is longer than the computer screen, and take corrective action when it goes beyond 30 LOC (lines of code). The bigger the haystack, the harder it is to find a needle.

    W5.4c

    Implementation → Code Quality → Readability → Basic → Avoid Deep Nesting

    Can improve code quality using technique: avoid deep nesting

    If you need more than 3 levels of indentation, you're screwed anyway, and should fix your program. --Linux 1.3.53 CodingStyle

    In particular, avoid arrowhead style code.

    A real code example:

    Bad

    int subsidy() {
        int subsidy;
        if (!age) {
            if (!sub) {
                if (!notFullTime) {
                    subsidy = 500;
                } else {
                    subsidy = 250;
                }
            } else {
                subsidy = 250;
            }
        } else {
            subsidy = -1;
        }
        return subsidy;
    }
    
      

    Good

    int calculateSubsidy() {
        int subsidy;
        if (isSenior) {
            subsidy = REJECT_SENIOR;
        } else if (isAlreadySubsidised) {
            subsidy = SUBSIDISED_SUBSIDY;
        } else if (isPartTime) {
            subsidy = FULLTIME_SUBSIDY * RATIO;
        } else {
            subsidy = FULLTIME_SUBSIDY;
        }
        return subsidy;
    }
    

    Bad

    def calculate_subs():
        if not age:
            if not sub:
                if not not_fulltime:
                    subsidy = 500
                else:
                    subsidy = 250
            else:
                subsidy = 250
        else:
            subsidy = -1
        return subsidy
    
      

    Good

    def calculate_subsidy():
        if is_senior:
            return REJECT_SENIOR
        elif is_already_subsidised:
            return SUBSIDISED_SUBSIDY
        elif is_parttime:
            return FULLTIME_SUBSIDY * RATIO
        else:
            return FULLTIME_SUBSIDY
    

    W5.4d

    Implementation → Code Quality → Readability → Basic → Avoid Complicated Expressions

    Can improve code quality using technique: avoid complicated expressions

    Avoid complicated expressions, especially those having many negations and nested parentheses. If you must evaluate complicated expressions, have it done in steps (i.e. calculate some intermediate values first and use them to calculate the final value).

    Example:

    Bad

    return ((length < MAX_LENGTH) || (previousSize != length)) && (typeCode == URGENT);
    

    Good

    
    boolean isWithinSizeLimit = length < MAX_LENGTH;
    boolean isSameSize = previousSize != length;
    boolean isValidCode = isWithinSizeLimit || isSameSize;
    
    boolean isUrgent = typeCode == URGENT;
    
    return isValidCode && isUrgent;
    

    Example:

    Bad

    return ((length < MAX_LENGTH) or (previous_size != length)) and (type_code == URGENT)
    

    Good

    is_within_size_limit = length < MAX_LENGTH
    is_same_size = previous_size != length
    is_valid_code = is_within_size_limit or is_same_size
    
    is_urgent = type_code == URGENT
    
    return is_valid_code and is_urgent
    

    The competent programmer is fully aware of the strictly limited size of his own skull; therefore he approaches the programming task in full humility, and among other things he avoids clever tricks like the plague. -- Edsger Dijkstra

    W5.4e

    Implementation → Code Quality → Readability → Basic → Avoid Magic Numbers

    Can improve code quality using technique: avoid magic numbers

    When the code has a number that does not explain the meaning of the number, we call that a "magic number" (as in "the number appears as if by magic"). Using a named constant makes the code easier to understand because the name tells us more about the meaning of the number.

    Example:

    Bad

    return 3.14236;
    ...
    return 9;
    
      

    Good

    static final double PI = 3.14236;
    static final int MAX_SIZE = 10;
    ...
    return PI;
    ...
    return MAX_SIZE-1;
    

    Note: Python does not have a way to make a variable a constant. However, you can use a normal variable with an ALL_CAPS name to simulate a constant.

    Bad

    return 3.14236
    ...
    return 9
    
      

    Good

    PI = 3.14236
    MAX_SIZE = 10
    ...
    return PI
    ...
    return MAX_SIZE-1
    

    Similarly, we can have ‘magic’ values of other data types.

    Bad

    "Error 1432"  // A magic string!
    

    In general, try to avoid any magic literals.

    W5.4f

    Implementation → Code Quality → Readability → Basic → Make the Code Obvious

    Can improve code quality using technique: make the code obvious

    Make the code as explicit as possible, even if the language syntax allows them to be implicit. Here are some examples:

    • [Java] Use explicit type conversion instead of implicit type conversion.
    • [Java, Python] Use parentheses/braces to show grouping even when they can be skipped.
    • [Java, Python] Use enumerations when a certain variable can take only a small number of finite values. For example, instead of declaring the variable 'state' as an integer and using values 0,1,2 to denote the states 'starting', 'enabled', and 'disabled' respectively, declare 'state' as type SystemState and define an enumeration SystemState that has values 'STARTING', 'ENABLED', and 'DISABLED'.

    W5.4g

    Implementation → Code Quality → Readability → Intermediate → Structure Code Logically

    Can improve code quality using technique: structure code logically

    Lay out the code so that it adheres to the logical structure. The code should read like a story. Just like we use section breaks, chapters and paragraphs to organize a story, use classes, methods, indentation and line spacing in your code to group related segments of the code. For example, you can use blank lines to group related statements together. Sometimes, the correctness of your code does not depend on the order in which you perform certain intermediary steps. Nevertheless, this order may affect the clarity of the story you are trying to tell. Choose the order that makes the story most readable.

    W5.4h

    Implementation → Code Quality → Readability → Intermediate → Do Not 'Trip Up' Reader

    Can improve code quality using technique: do not 'trip up' reader

    Avoid things that would make the reader go ‘huh?’, such as,

    • unused parameters in the method signature
    • similar things that look different
    • different things that look similar
    • multiple statements in the same line
    • data flow anomalies such as, pre-assigning values to variables and modifying it without any use of the pre-assigned value

    W5.4i

    Implementation → Code Quality → Readability → Intermediate → Practice KISSing

    Can improve code quality using technique: practice kissing

    As the old adage goes, "keep it simple, stupid” (KISS). Do not try to write ‘clever’ code. For example, do not dismiss the brute-force yet simple solution in favor of a complicated one because of some ‘supposed benefits’ such as 'better reusability' unless you have a strong justification.

    Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it. --Brian W. Kernighan

    Programs must be written for people to read, and only incidentally for machines to execute. --Abelson and Sussman

    W5.4j

    Implementation → Code Quality → Readability → Intermediate → Avoid Premature Optimizations

    Can improve code quality using technique: avoid premature optimizations

    Optimizing code prematurely has several drawbacks:

    • We may not know which parts are the real performance bottlenecks. This is especially the case when the code undergoes transformations (e.g. compiling, minifying, transpiling, etc.) before it becomes an executable. Ideally, you should use a profiler tool to identify the actual bottlenecks of the code first, and optimize only those parts.
    • Optimizing can complicate the code, affecting correctness and understandability
    • Hand-optimized code can be harder for the compiler to optimize (the simpler the code, the easier for the compiler to optimize it). In many cases a compiler can do a better job of optimizing the runtime code if you don't get in the way by trying to hand-optimize the source code.

    A popular saying in the industry is make it work, make it right, make it fast which means in most cases getting the code to perform correctly should take priority over optimizing it. If the code doesn't work correctly, it has no value on matter how fast/efficient it it.

    Premature optimization is the root of all evil in programming. --Donald Knuth

    Note that there are cases where optimizing takes priority over other things e.g. when writing code for resource-constrained environments. This guideline simply a caution that you should optimize only when it is really needed.

    W5.4k

    Implementation → Code Quality → Readability → Intermediate → SLAP Hard

    Can improve code quality using technique: SLAP hard

    Avoid varying the level of abstraction within a code fragment. Note: The Productive Programmer (by Neal Ford) calls this the SLAP principle i.e. Single Level of Abstraction Per method.

    Example:

    Bad (readData(); and salary = basic*rise+1000; are at different levels of abstraction)

    readData();
    salary = basic*rise+1000;
    tax = (taxable?salary*0.07:0);
    displayResult();
    

    Good (all statements are at the same level of abstraction)

    readData();
    processData();
    displayResult();
    

    Design → Design Fundamentals → Abstraction →

    What

    Abstraction is a technique for dealing with complexity. It works by establishing a level of complexity we are interested in, and suppressing the more complex details below that level.

    The guiding principle of abstraction is that only details that are relevant to the current perspective or the task at hand needs to be considered. As most programs are written to solve complex problems involving large amounts of intricate details, it is impossible to deal with all these details at the same time. That is where abstraction can help.

    Data abstraction: abstracting away the lower level data items and thinking in terms of bigger entities

    Within a certain software component, we might deal with a user data type, while ignoring the details contained in the user data item such as name, and date of birth. These details have been ‘abstracted away’ as they do not affect the task of that software component.

    Control abstraction: abstracting away details of the actual control flow to focus on tasks at a higher level

    print(“Hello”) is an abstraction of the actual output mechanism within the computer.

    Abstraction can be applied repeatedly to obtain progressively higher levels of abstractions.

    An example of different levels of data abstraction: a File is a data item that is at a higher level than an array and an array is at a higher level than a bit.

    An example of different levels of control abstraction: execute(Game) is at a higher level than print(Char) which is at a higher than an Assembly language instruction MOV.

    Abstraction is a general concept that is not limited to just data or control abstractions.

    Some more general examples of abstraction:

    • An OOP class is an abstraction over related data and behaviors.
    • An architecture is a higher-level abstraction of the design of a software.
    • Models (e.g., UML models) are abstractions of some aspect of reality.

    W5.4l

    Implementation → Code Quality → Readability → Advanced → Make the Happy Path Prominent

    Can improve code quality using technique: make the happy path prominent

    The happy path (i.e. the execution path taken when everything goes well) should be clear and prominent in your code. Restructure the code to make the happy path unindented as much as possible. It is the ‘unusual’ cases that should be indented. Someone reading the code should not get distracted by alternative paths taken when error conditions happen. One technique that could help in this regard is the use of guard clauses.

    Example:

    Bad

    if (!isUnusualCase) {  //detecting an unusual condition
        if (!isErrorCase) {
            start();    //main path
            process();
            cleanup();
            exit();
        } else {
            handleError();
        }
    } else {
        handleUnusualCase(); //handling that unusual condition
    }
    

    In the code above,

    • Unusual condition detection is separated from their handling.
    • Main path is nested deeply.

    Good

    if (isUnusualCase) { //Guard Clause
        handleUnusualCase();
        return;
    }
    
    if (isErrorCase) { //Guard Clause
        handleError();
        return;
    }
    
    start();
    process();
    cleanup();
    exit();
    

    In contrast, the above code

    • deals with unusual conditions as soon as they are detected so that the reader doesn't have to remember them for long.
    • keeps the main path un-indented.

    Naming

    W5.4m

    Implementation → Code Quality → Naming → Introduction

    Can explain the need for good names in code

    Proper naming improves the readability. It also reduces bugs caused by ambiguities regarding the intent of a variable or a method.

    There are only two hard things in Computer Science: cache invalidation and naming things. -- Phil Karlton

    W5.4n

    Implementation → Code Quality → Naming → Basic → Use Nouns for Things and Verbs for Actions

    Can improve code quality using technique: use nouns for things and verbs for actions

    Every system is built from a domain-specific language designed by the programmers to describe that system. Functions are the verbs of that language, and classes are the nouns. ― Robert C. Martin, Clean Code: A Handbook of Agile Software Craftsmanship

    Use nouns for classes/variables and verbs for methods/functions.

    Examples:

    Name for a Bad Good
    Class CheckLimit LimitChecker
    method result() calculate()

    Distinguish clearly between single-valued and multivalued variables.

    Examples:

    Good

    Person student;
    ArrayList<Person> students;
    

    Good

    name = 'Jim'
    names = ['Jim', 'Alice']
    

    W5.4o

    Implementation → Code Quality → Naming → Basic → Use Standard Words

    Can improve code quality using technique: use standard words

    Use correct spelling in names. Avoid 'texting-style' spelling. Avoid foreign language words, slang, and names that are only meaningful within specific contexts/times e.g. terms from private jokes, a TV show currently popular in your country

    W5.4p

    Implementation → Code Quality → Naming → Intermediate → Use Name to Explain

    Can improve code quality using technique: use name to explain

    A name is not just for differentiation; it should explain the named entity to the reader accurately and at a sufficient level of detail.

    Examples:

    Bad Good
    processInput() (what 'process'?) removeWhiteSpaceFromInput()
    flag isValidInput
    temp

    If the name has multiple words, they should be in a sensible order.

    Examples:

    Bad Good
    bySizeOrder() orderBySize()

    Imagine going to the doctor's and saying "My eye1 is swollen"! Don’t use numbers or case to distinguish names.

    Examples:

    Bad Bad Good
    value1, value2 value, Value originalValue, finalValue

    W5.4q

    Implementation → Code Quality → Naming → Intermediate → Not Too Long, Not Too Short

    Can improve code quality using technique: not too long, not too short

    While it is preferable not to have lengthy names, names that are 'too short' are even worse. If you must abbreviate or use acronyms, do it consistently. Explain their full meaning at an obvious location.

    W5.4r

    Implementation → Code Quality → Naming → Intermediate → Avoid Misleading Names

    Can improve code quality using technique: avoid misleading names

    Related things should be named similarly, while unrelated things should NOT.

    Example: Consider these variables

    • colorBlack: hex value for color black
    • colorWhite: hex value for color white
    • colorBlue: number of times blue is used
    • hexForRed: hex value for color red

    This is misleading because colorBlue is named similar to colorWhite and colorBlack but has a different purpose while hexForRed is named differently but has very similar purpose to the first two variables. The following is better:

    • hexForBlack hexForWhite hexForRed
    • blueColorCount

    Avoid misleading or ambiguous names (e.g. those with multiple meanings), similar sounding names, hard-to-pronounce ones (e.g. avoid ambiguities like "is that a lowercase L, capital I or number 1?", or "is that number 0 or letter O?"), almost similar names.

    Examples:

    Bad Good Reason
    phase0 phaseZero Is that zero or letter O?
    rwrLgtDirn rowerLegitDirection Hard to pronounce
    right left wrong rightDirection leftDirection wrongResponse right is for 'correct' or 'opposite of 'left'?
    redBooks readBooks redColorBooks booksRead red and read (past tense) sounds the same
    FiletMignon egg If the requirement is just a name of a food, egg is a much easier to type/say choice than FiletMignon

    Unsafe Practices

    W5.4s

    Implementation → Code Quality → Error-Prone Practices → Introduction

    Can explain the need for avoiding error-prone shortcuts

    It is safer to use language constructs in the way they are meant to be used, even if the language allows shortcuts. Some such coding practices are common sources of bugs. Know them and avoid them.

    W5.4t

    Implementation → Code Quality → Error-Prone Practices → Basic → Use the Default Branch

    Can improve code quality using technique: use the default branch

    Always include a default branch in case statements.

    Furthermore, use it for the intended default action and not just to execute the last option. If there is no default action, you can use the 'default' branch to detect errors (i.e. if execution reached the default branch, throw an exception). This also applies to the final else of an if-else construct. That is, the final else should mean 'everything else', and not the final option. Do not use else when an if condition can be explicitly specified, unless there is absolutely no other possibility.

    Bad

    if (red) print "red";
    else print "blue";
    

    Good

    if (red) print "red";
    else if (blue) print "blue";
    else error("incorrect input");
    

    W5.4u

    Implementation → Code Quality → Error-Prone Practices → Basic → Don't Recycle Variables or Parameters

    Can improve code quality using technique: don't recycle variables or parameters

    • Use one variable for one purpose. Do not reuse a variable for a different purpose other than its intended one, just because the data type is the same.
    • Do not reuse formal parameters as local variables inside the method.

    Bad

    double computeRectangleArea(double length, double width) {
        length = length * width;
        return length;
    }
    
    

    Good

    double computeRectangleArea(double length, double width) {
        double area;
        area = length * width;
        return area;
    }
    

    W5.4v

    Implementation → Code Quality → Error-Prone Practices → Basic → Avoid Empty Catch Blocks

    Can improve code quality using technique: avoid empty catch blocks

    Never write an empty catch statement. At least give a comment to explain why the catch block is left empty.

    W5.4w

    Implementation → Code Quality → Error-Prone Practices → Basic → Delete Dead Code

    Can improve code quality using technique: delete dead code

    We all feel reluctant to delete code we have painstakingly written, even if we have no use for that code any more ("I spent a lot of time writing that code; what if we need it again?"). Consider all code as baggage you have to carry; get rid of unused code the moment it becomes redundant. If you need that code again, simply recover it from the revision control tool you are using. Deleting code you wrote previously is a sign that you are improving.

    W5.4x

    Implementation → Code Quality → Error-Prone Practices → Intermediate → Minimize Scope of Variables

    Can improve code quality using technique: minimise scope of variables

    Minimize global variables. Global variables may be the most convenient way to pass information around, but they do create implicit links between code segments that use the global variable. Avoid them as much as possible.

    Define variables in the least possible scope. For example, if the variable is used only within the if block of the conditional statement, it should be declared inside that if block.

    The most powerful technique for minimizing the scope of a local variable is to declare it where it is first used. -- Effective Java, by Joshua Bloch

    Resources:

    W5.4y

    Implementation → Code Quality → Error-Prone Practices → Intermediate → Minimize Code Duplication

    Can improve code quality using technique: minimize code duplication

    Code duplication, especially when you copy-paste-modify code, often indicates a poor quality implementation. While it may not be possible to have zero duplication, always think twice before duplicating code; most often there is a better alternative.

    This guideline is closely related to the DRY Principle .

    Principles →

    DRY Principle

    DRY (Don't Repeat Yourself) Principle: Every piece of knowledge must have a single, unambiguous, authoritative representation within a system The Pragmatic Programmer, by Andy Hunt and Dave Thomas

    This principle guards against duplication of information.

    The functionality implemented twice is a violation of the DRY principle even if the two implementations are different.

    The value a system-wide timeout being defined in multiple places is a violation of DRY.


    Code Comments

    W5.4z

    Implementation → Code Quality → Comments → Introduction

    Can explain the need for commenting minimally but sufficiently

    Good code is its own best documentation. As you’re about to add a comment, ask yourself, ‘How can I improve the code so that this comment isn’t needed?’ Improve the code and then document it to make it even clearer. --Steve McConnell, Author of Clean Code

    Some think commenting heavily increases the 'code quality'. This is not so. Avoid writing comments to explain bad code. Improve the code to make it self-explanatory.

    W5.4A

    Implementation → Code Quality → Comments → Basic → Do Not Repeat the Obvious

    Can improve code quality using technique: do not repeat the obvious

    If the code is self-explanatory, refrain from repeating the description in a comment just for the sake of 'good documentation'.

    Bad

    // increment x
    x++;
    
    //trim the input
    trimInput();
    

    Bad

    // increment x
    x = x + 1
    
    //trim the input
    trim_input()
    

    W5.4B

    Implementation → Code Quality → Comments → Basic → Write to the Reader

    Can improve code quality using technique: write to the reader

    Do not write comments as if they are private notes to yourself. Instead, write them well enough to be understood by another programmer. One type of comments that is almost always useful is the header comment that you write for a class or an operation to explain its purpose.

    Examples:

    Bad Reason: this comment will only make sense to the person who wrote it

    // a quick trim function used to fix bug I detected overnight
    void trimInput(){
        ....
    }
    

    Good

    /** Trims the input of leading and trailing spaces */
    void trimInput(){
        ....
    }
    

    Bad Reason: this comment will only make sense to the person who wrote it

    # a quick trim function used to fix bug I detected overnight
    def trim_input():
        ...
    

    Good

    def trim_input():
    """Trim the input of leading and trailing spaces"""
        ...
    

    W5.4C

    Implementation → Code Quality → Comments → Intermediate → Explain WHAT and WHY, not HOW

    Can improve code quality using technique: explain what and why, not how

    Comments should explain what and why aspect of the code, rather than the how aspect.

    What : The specification of what the code supposed to do. The reader can compare such comments to the implementation to verify if the implementation is correct

    Example: This method is possibly buggy because the implementation does not seem to match the comment. In this case the comment could help the reader to detect the bug.

    /** Removes all spaces from the {@code input} */
    void compact(String input){
        input.trim();
    }
    

    Why : The rationale for the current implementation.

    Example: Without this comment, the reader will not know the reason for calling this method.

    // Remove spaces to comply with IE23.5 formatting rules
    compact(input);
    

    How : The explanation for how the code works. This should already be apparent from the code, if the code is self-explanatory. Adding comments to explain the same thing is redundant.

    Example:

    Bad Reason: Comment explains how the code works.

    // return true if both left end and right end are correct or the size has not incremented
    return (left && right) || (input.size() == size);
    

    Good Reason: Code refactored to be self-explanatory. Comment no longer needed.

    
    boolean isSameSize = (input.size() == size) ;
    return (isLeftEndCorrect && isRightEndCorrect) || isSameSize;
    

    [W5.5] Refactoring

    W5.5a

    Implementation → Refactoring → What

    Can explain refactoring

    The first version of the code you write may not be of production quality. It is OK to first concentrate on making the code work, rather than worry over the quality of the code, as long as you improve the quality later. This process of improving a program's internal structure in small steps without modifying its external behavior is called refactoring.

    • Refactoring is not rewriting: Discarding poorly-written code entirely and re-writing it from scratch is not refactoring because refactoring needs to be done in small steps.
    • Refactoring is not bug fixing: By definition, refactoring is different from bug fixing or any other modifications that alter the external behavior (e.g. adding a feature) of the component in concern.

    Improving code structure can have many secondary benefits: e.g.

    • hidden bugs become easier to spot
    • improve performance (sometimes, simpler code runs faster than complex code because simpler code is easier for the compiler to optimize).

    Given below are two common refactorings ( more).

    Refactoring Name: Consolidate Duplicate Conditional Fragments

    Situation: The same fragment of code is in all branches of a conditional expression.

    Method: Move it outside of the expression.

    Example:

    if (isSpecialDeal()) {
        total = price * 0.95;
        send();
    } else {
        total = price * 0.98;
        send();
    }
    
     → 
    if (isSpecialDeal()){
        total = price * 0.95;
    } else {
        total = price * 0.98;
    }
    send();
    
    
    if is_special_deal:
        total = price * 0.95
        send()
    else:
        total = price * 0.98
        send()
    
     → 
    if is_special_deal:
        total = price * 0.95
    else:
        total = price * 0.98
        
    send()
    

    Refactoring Name: Extract Method

    Situation: You have a code fragment that can be grouped together.

    Method: Turn the fragment into a method whose name explains the purpose of the method.

    Example:

    void printOwing() {
        printBanner();
    
        //print details
        System.out.println("name:	" + name);
        System.out.println("amount	" + getOutstanding());
    }
    

    void printOwing() {
        printBanner();
        printDetails(getOutstanding());
    }
    
    void printDetails (double outstanding) {
        System.out.println("name:	" + name);
        System.out.println("amount	" + outstanding);
    }
    
    def print_owing():
        print_banner()
    
        //print details
        print("name:	" + name)
        print("amount	" + get_outstanding())
    

    def print_owing():
        print_banner()
        print_details(get_outstanding())
    
    def print_details(amount):
        print("name:	" + name)
        print("amount	" + amount)
    

    Some IDEs have built in support for basic refactorings such as automatically renaming a variable/method/class in all places it has been used.

    Refactoring, even if done with the aid of an IDE, may still result in regressions. Therefore, each small refactoring should be followed by regression testing.

    Choose the correct statements

    • a. Refactoring can improve understandability
    • b. Refactoring can uncover bugs
    • c. Refactoring can result in better performance
    • d. Refactoring can change the number of methods/classes

    a, b, c, d

    Explanation:

    • (a, b, c) Although the primary aim of refactoring is to improve internal code structure, there are other secondary benefits.
    • (d) Some refactorings result in adding/removing methods/classes.

    Do you agree with the following statement? Justify your answer.

    Statement: Whenever we refactor code to fix bugs, we need not do regression testing if the bug fix was minor.

    There are two flaws in the given statement.

    DISAGREE.

    1. Even a minor change can have major repercussions on the system. We MUST do regression testing after each change, no matter how minor it is.
    2. Fixing bugs is technically not refactoring.

    Explain what is refactoring and why it is not the same as rewriting, bug fixing, or adding features.

    W5.5b

    Tools → Intellij IDEA → Refactoring

    Can use automated refactoring features of the IDE

    This video explains how to automate the 'Extract parameter' refactoring using Intellij IDEA. Most other refactorings available works similarly. i.e. select the code to refactorfind the refactoring in the context menu or use the keyboard shortcut.

    Here's another video explaining how to change a method signature as part of refactoring.

    W5.5c

    Implementation → Refactoring → How

    Can apply some basic refactoring

    Given below are some more commonly used refactorings. A more comprehensive list is available at refactoring-catalog .

    1. Consolidate Conditional Expression
    2. Decompose Conditional
    3. Inline Method
    4. Remove Double Negative
    5. Replace Magic Literal
    6. Replace Nested Conditional with Guard Clauses
    7. Replace Parameter with Explicit Methods
    8. Reverse Conditional
    9. Split Loop
    10. Split Temporary Variable

    W5.5d

    Implementation → Refactoring → When

    Can decide when to apply a given refactoring

    We know that it is important to refactor frequently so as to avoid the accumulation of ‘messy’ code which might get out of control. But how much refactoring is too much refactoring? It is too much refactoring when the benefits no longer justify the cost. The costs and the benefits depend on the context. That is why some refactorings are ‘opposites’ of each other (e.g. extract method vs inline method).

    ‘Extract method’ and ‘Inline method’ refactorings

    a

    [W5.6] Assertions

    W5.6a :

    Implementation → Error Handling → Assertions → What

    Can explain assertions

    Assertions are used to define assumptions about the program state so that the runtime can verify them. An assertion failure indicates a possible bug in the code because the code has resulted in a program state that violates an assumption about how the code should behave.

    An assertion can be used to express something like when the execution comes to this point, the variable v cannot be null.

    If the runtime detects an assertion failure, it typically take some drastic action such as terminating the execution with an error message. This is because an assertion failure indicates a possible bug and the sooner the execution stops, the safer it is.

    In the Java code below, suppose we set an assertion that timeout returned by Config.getTimeout() is greater than 0. Now, if the Config.getTimeout() returned -1 in a specific execution of this line, the runtime can detect it as a assertion failure -- i.e. an assumption about the expected behavior of the code turned out to be wrong which could potentially be the result of a bug -- and take some drastic action such as terminating the execution.

    int timeout = Config.getTimeout(); 
    

    W5.6b :

    Implementation → Error Handling → Assertions → How

    Can use assertions

    Use the assert keyword to define assertions.

    This assertion will fail with the message x should be 0 if x is not 0 at this point.

    x = getX();
    assert x == 0 : "x should be 0";
    ...
    

    Assertions can be disabled without modifying the code.

    java -enableassertions HelloWorld (or java -ea HelloWorld) will run HelloWorld with assertions enabled while java -disableassertions HelloWorld will run it without verifying assertions.

    Java disables assertions by default. This could create a situation where you think all assertions are being verified as true while in fact they are not being verified at all. Therefore, remember to enable assertions when you run the program if you want them to be in effect.

    Enable assertions in Intellij (how?) and get an assertion to fail temporarily (e.g. insert an assert false into the code temporarily) to confirm assertions are being verified.

    Java assert vs JUnit assertions: They are similar in purpose but JUnit assertions are more powerful and customized for testing. In addition, JUnit assertions are not disabled by default. We recommend you use JUnit assertions in test code and Java assert in functional code.

    Tutorials:

    Best practices:

    W5.6c :

    Implementation → Error Handling → Assertions → When

    Can use assertions optimally

    It is recommended that assertions be used liberally in the code. Their impact on performance is considered low and worth the additional safety they provide.

    Do not use assertions to do work because assertions can be disabled. If not, your program will stop working when assertions are not enabled.

    The code below will not invoke the writeFile() method when assertions are disabled. If that method is performing some work that is necessary for your program, your program will not work correctly when assertions are disabled.

    ...
    assert writeFile() : "File writing is supposed to return true";
    

    Assertions are suitable for verifying assumptions about Internal Invariants, Control-Flow Invariants, Preconditions, Postconditions, and Class Invariants. Refer to [Programming with Assertions (second half)] to learn more.

    Exceptions and assertions are two complementary ways of handling errors in software but they serve different purposes. Therefore, both assertions and exceptions should be used in code.

    • The raising of an exception indicates an unusual condition created by the user (e.g. user inputs an unacceptable input) or the environment (e.g., a file needed for the program is missing).
    • An assertion failure indicates the programmer made a mistake in the code (e.g., a null value is returned from a method that is not supposed to return null under any circumstances).

    A Calculator program crashes with an ‘assertion failure’ message when you try to find the square root of a negative number.

    (c)

    Explanation: An assertion failure indicates a bug in the code. (b) is not acceptable because of the word "terminated". The application should not fail at all for this input. But it could have used an exception to handle the situation internally.

    Which statements are correct?

    • a. Use assertions to indicate the programmer messed up; Use exceptions to indicate the user or the environment messed up.
    • b. Use exceptions to indicate the programmer messed up; Use assertions to indicate the user or the environment messed up.

    (a)

    [W5.7] Java: streams

    W5.7a :

    Tools → Java → Streams: Basic

    Can use Java8 streams

    Java 8 introduced a number of new features (e.g. Lambdas, Streams) that are not trivial to learn but also extremely useful to know.

    Here is an overview of new Java 8 features .(written by Benjamin Winterberg)

    Tutorials:

    A video tutorial by well-known Java coach Venkat Subramaniam

    [W5.8] Continuous Integration/Deployment

    W5.8a

    Implementation → Integration → Introduction → What

    Can explain integration

    Combining parts of a software product to form a whole is called integration. It is also one of the most troublesome tasks and it rarely goes smoothly.

    W5.8b

    Implementation → Integration → Build Automation → What

    Can explain build automation tools

    Build automation tools automate the steps of the build process, usually by means of build scripts.

    In a non-trivial project, building a product from source code can be a complex multi-step process. For example, it can include steps such as to pull code from the revision control system, compile, link, run automated tests, automatically update release documents (e.g. build number), package into a distributable, push to repo, deploy to a server, delete temporary files created during building/testing, email developers of the new build, and so on. Furthermore, this build process can be done ‘on demand’, it can be scheduled (e.g. every day at midnight) or it can be triggered by various events (e.g. triggered by a code push to the revision control system).

    Some of these build steps such as to compile, link and package are already automated in most modern IDEs. For example, several steps happen automatically when the ‘build’ button of the IDE is clicked. Some IDEs even allow customization to this build process to some extent.

    However, most big projects use specialized build tools to automate complex build processes.

    Some popular build tools relevant to Java developers: Gradle, Maven, Apache Ant, GNU Make

    Some other build tools : Grunt (JavaScript), Rake (Ruby)

    Some build tools also serve as dependency management tools. Modern software projects often depend on third party libraries that evolve constantly. That means developers need to download the correct version of the required libraries and update them regularly. Therefore, dependency management is an important part of build automation. Dependency Management tools can automate that aspect of a project.

    Maven and Gradle, in addition to managing the build process, can play the role of dependency management tools too.

    Gradle_is used used for,

    • a. better revision control
    • b. build automation
    • c. UML diagramming
    • d. project collaboration

    (b)

    W5.8c

    Implementation → Integration → Build Automation → Continuous Integration and Continuous Deployment

    Can explain continuous integration and continuous deployment

    An extreme application of build automation is called continuous integration (CI) in which integration, building, and testing happens automatically after each code change.

    A natural extension of CI is Continuous Deployment (CD) where the changes are not only integrated continuously, but also deployed to end-users at the same time.

    Some examples of CI/CD tools: Travis, Jenkins, Appveyor, CircleCI