TIC2002 (2018)
  • Flat (current format)
  •     Nested
  • Schedule
  • Textbook
  • Admin Info
  • Report Bugs
  • Slack
  • Forum
  • Instructors
  • Announcements
  • File Submissions
  • Tutorial Schedule
  • Team IDs
  • Java Coding Standard
  • samplerepo-things
  • Addressbook-level1
  • Addressbook-level2
  • Addressbook-level3
  • Addressbook-level4
  • Projects List
  • Week 12 [Nov 5]

    Todo

    Admin info to read:

    Admin Project: mid-v1.4 [week 12]

    Tweak as per peer-testing results, draft Project Portfolio Page, practice product demo.

    Project Management:

    • Freeze features around this time. Ensure the current product have all the features you intend to release at v1.4. Adding major changes after this point is risky. The remaining time is better spent fixing problems discovered late or on fine-tuning the product.

    Product:

    • Consider increasing code coverage by adding more tests if it is lower than the level you would like it to be. Take note of our expectation on test code.
    • After you have sufficient code coverage, fix remaining code quality problems and bring up the quality to your target level.
     
    • There is no requirement for a minimum coverage level. Note that in a production environment you are often required to have at least 90% of the code covered by tests. In this project, it can be less. The less coverage you have, the higher the risk of regression bugs, which will cost marks if not fixed before the final submission.
    • You must write some tests so that we can evaluate your ability to write tests.
    • How much of each type of testing should you do? We expect you to decide. You learned different types of testing and what they try to achieve. Based on that, you should decide how much of each type is required. Similarly, you can decide to what extent you want to automate tests, depending on the benefits and the effort required.
    • Applying TDD is optional. If you plan to test something, it is better to apply TDD because TDD ensures that you write functional code in a testable way. If you do it the normal way, you often find that it is hard to test the functional code because the code has low testability.

    Relevant: [Admin Project Assessment → Code Quality Tips ]

     
    • Ensure your code has at least some evidence of these (see here for more info)

      • logging
      • exceptions
      • assertions
      • defensive coding
    • Ensure there are no coding standard violations  e.g. all boolean variables/methods sounds like booleans. Checkstyle can prevent only some coding standard violations; others need to be checked manually.

    • Ensure SLAP is applied at a reasonable level. Long methods or deeply-nested code are symptoms of low-SLAP may be counted against your code quality.

    • Reduce code duplications  i.e. if there multiple blocks of code that vary only in minor ways, try to extract out similarities into one place, especially in test code.

    • In addition, try to apply as many of the code quality guidelines covered in the module as much as you can.

     

    Code Quality

    Introduction

    Basic

    Can explain the importance of code quality

    Always code as if the person who ends up maintaining your code will be a violent psychopath who knows where you live. -- Martin Golding

    Production code needs to be of high quality . Given how the world is becoming increasingly dependent of software, poor quality code is something we cannot afford to tolerate.

    Code being used in an actual product with actual users

    Guideline: Maximise 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
    

    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.

    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.

    Example:

    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

    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!
    

    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'.

    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.

    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 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

    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

    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.

    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();
    salary = basic*rise+1000;
    tax = (taxable?salary*0.07:0);
    displayResult();
    

    Good

    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.

    Ignoring lower level data items and thinking in terms of bigger entities is called data abstraction.

    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 abstracts away details of the actual control flow to focus on tasks at a simplified 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.

    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.

    Guideline: Follow a Standard

    Introduction

    Can explain the need for following a standard

    One essential way to improve code quality is to follow a consistent style. That is why software engineers follow a strict coding standard (aka style guide).

    The aim of a coding standard is to make the entire code base look like it was written by one person. A coding standard is usually specific to a programming language and specifies guidelines such as the location of opening and closing braces, indentation styles and naming styles (e.g. whether to use Hungarian style, Pascal casing, Camel casing, etc.). It is important that the whole team/company use the same coding standard and that standard is not generally inconsistent with typical industry practices. If a company's coding standards is very different from what is used typically in the industry, new recruits will take longer to get used to the company's coding style.

    💡 IDEs can help to enforce some parts of a coding standard e.g. indentation rules.

    What is the recommended approach regarding coding standards?

    c

    What is the aim of using a coding standard? How does it help?

    Basic

    Can follow simple mechanical style rules

    Learn basic guidelines of the Java coding standard (by OSS-Generic)

    Consider the code given below:

    import java.util.*;
    
    public class Task {
        public static final String descriptionPrefix = "description: ";
        private String description;
        private boolean important;
        List<String> pastDescription = new ArrayList<>(); // a list of past descriptions
    
        public Task(String d) {
          this.description = d;
          if (!d.isEmpty())
              this.important = true;
        }
    
        public String getAsXML() { return "<task>"+description+"</task>"; }
    
        /**
         * Print the description as a string.
         */
        public void printingDescription(){ System.out.println(this); }
    
        @Override
        public String toString() { return descriptionPrefix + description; }
    }
    

    In what ways the code violate the basic guidelines (i.e., those marked with one ⭐️) of the OSS-Generic Java Coding Standard given here?

    Here are three:

    • descriptionPrefix is a constant and should be named DESCRIPTION_PREFIX
    • method name printingDescription() should be named as printDescription()
    • boolean variable important should be named to sound boolean e.g., isImportant

    There are many more.

    Intermediate

    Can follow intermediate style rules

    Go through the provided Java coding standard and learn the intermediate style rules.

    According to the given Java coding standard, which one of these is not a good name?

    b

    Explanation: checkWeight is an action. Naming variables as actions makes the code harder to follow. isWeightValid may be a better name.

    Repeat the exercise in the panel below but also find violations of intermediate level guidelines.

    Consider the code given below:

    import java.util.*;
    
    public class Task {
        public static final String descriptionPrefix = "description: ";
        private String description;
        private boolean important;
        List<String> pastDescription = new ArrayList<>(); // a list of past descriptions
    
        public Task(String d) {
          this.description = d;
          if (!d.isEmpty())
              this.important = true;
        }
    
        public String getAsXML() { return "<task>"+description+"</task>"; }
    
        /**
         * Print the description as a string.
         */
        public void printingDescription(){ System.out.println(this); }
    
        @Override
        public String toString() { return descriptionPrefix + description; }
    }
    

    In what ways the code violate the basic guidelines (i.e., those marked with one ⭐️) of the OSS-Generic Java Coding Standard given here?

    Here are three:

    • descriptionPrefix is a constant and should be named DESCRIPTION_PREFIX
    • method name printingDescription() should be named as printDescription()
    • boolean variable important should be named to sound boolean e.g., isImportant

    There are many more.

    Here's one you are more likely to miss:

    • * Print the description as a string.* Prints the description as a string.

    There are more.

    Guideline: Name Well

    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

    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

    student = Person('Jim')
    students = [Person('Jim'), Person('Alice')]
    

    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

    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

    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.

    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

    Guideline: Avoid Unsafe Shortcuts

    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.

    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");
    

    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;
    }
    

    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.

    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.

    Intermediate

    Minimise 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:

    Minimise Code Duplication

    Can improve code quality using technique: minimise 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.

    Supplmentary → 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.

    Guideline: Comment Minimally, but Sufficiently

    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.

    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();
    

    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 self. 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"""
        ...
    

    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;
    

    null

    Documentation:

    • Update documentation to match the product.

    • Create the first version of your Project Portfolio Page (PPP). Reason: Each member needs to create a PPP to describe your contribution to the project. Creating a PPP takes a significant effort; it is too risky to leave it to the last week of the project.

    Relevant: [Admin Project → Deliverables → Project Portfolio Page ]

     

    At the end of the project each student is required to submit a Project Portfolio Page.

    • Objective:

      • For you to use  (e.g. in your resume) as a well-documented data point of your SE experience
      • For us to use as a data point to evaluate your,
        • contributions to the project
        • your documentation skills
    • What to include:

      • Main things to include:
        • links to collated code
        • features you implemented  (include relevant extracts from the user guide and the developer guide)
        • features you propose to implement in future  (include relevant extracts from the user guide and the developer guide if applicable)
          Note: the purpose of allowing you to include proposed features is to provide you more flexibility to show your documentation skills.  e.g. you can bring in a proposed feature just to give you an opportunity to use a UML diagram type not used by the actual features.
        • other significant contributions to the project  e.g. tweaks to existing features, setting up project tools
      • Other things you can include:
        • Evidence of helping others  e.g. responses you posted in our forum, bugs you reported in other team's products,
        • Evidence of technical leadership  e.g. offering features for others to reuse, evidence of those features being reused by others
      • If you plan to use the PPP in your Resume, you can also include your SE work outside of the module (will not be graded)
    • Format:

      • Page limit: If you have more content than the limit given below, shorten (or omit some content) so that you do not exceed the page limit. Having too much content in the PPP will be viewed unfavorably during grading. Note: the page limits given below are after converting to PDF format. The actual amount of content you require is actually less than what these numbers suggest because the HTML → PDF conversion adds a lot of spacing around content.
        Content Limit
        Description in point form 0.5-1
        Extracts from the User Guide 1-3
        Extracts from the Developer Guide 3-6
        Total 5-10
      • Follow the example in the AddressBook-Level4.
      • 💡 You can use the Asciidoc's include feature to include sections from the developer guide or the user guide in your PPP. Follow the example in the sample.
      • ❗️ It is assumed that all contents in the PPP were written primarily by you. If any section is written by someone else  e.g. someone else wrote described the feature in the User Guide but you implemented the feature, clearly state that the section was written by someone else  (e.g. Start of Extract [from: User Guide] written by Jane Doe).  Reason: Your writing skills will be evaluated based on the PPP

    Demo:

    • Do a product demo to serve as a rehearsal for the final project demo at v1.4
      • Follow final demo instructions as much as possible.
      • Cover all features, not just the ones added in the recent iteration.
      • Try to make it a 'well prepared' demo i.e., know in advance exactly what you'll do in the demo.
     
    • Duration: Strictly (teamSize x 3) + 1 minutes  e.g. 17 minutes for a 5-person team. Exceeding this limit will be penalized. The extra minute is for the first speaker to give an overview of the product.

    • Target audience: Assume you are giving a demo to a higher-level manager of your company, to brief him/her on the current capabilities of the product. This is the first time they are seeing the new product you developed but they are familiar with the AddressBook-level4 (AB4) product.

    • Scope:

      • Each person should demo the enhancements they added. However, it's ok for one member to do all the typing.
      • Subjected to the constraint mentioned in the previous point, as far as possible, organize the demo to present a cohesive picture of the product as a whole, presented in a logical order.  Remember to explain the profile of the target user profile and value proposition early in the demo.
      • It is recommended you showcase how the feature improves the user’s life rather than simply describe each feature.
      • No need to cover design/implementation details as the manager is not interested in those details.
      • Mention features you inherited from AB4 only if they are needed to explain your new features.  Reason: existing features will not earn you marks, and the audience is already familiar with AB4 features.
    • Structure:

      • Demo the product using the same executable you submitted, on your own laptop, using the TV.
      • It can be a sitting down demo: You'll be demonstrating the features using the TV while sitting down. But you may stand around the TV if you prefer that way.
      • It will be uninterrupted demo: The audience members will not interrupt you during the demo. That means you should finish within the given time.
      • The app should be populated with a significant amount of realistic data at the start.  e.g at least 20 contacts.
      • Dress code : The level of formality is up to you, but it is recommended that the whole team dress at the same level.
    • Optimizing the time:

      • Spend as much time as possible on demonstrating the actual product. Not recommended to use slides (if you do, use them sparingly) or videos or lengthy narrations.
        Avoid skits, re-enactments, dramatizations etc. This is not a sales pitch or an informercial. While you need to show how a user use the product to get value, but you don’t need to act like an imaginary user. For example, [Instead of this]Jim get’s a call from boss. "Ring ring", "hello", "oh hi Jim, can we postpone the meeting?" "Sure". Jim hang up and curses the boss under his breath. Now he starts typing ..etc. [do this] If Jim needs to postpone the meeting, he can type …
        It’s not that dramatization is bad or we don’t like it. We simply don’t have enough time for it.
        Note that CS2101 demo requirements may differ. Different context → Different requirements.
      • Rehearse the steps well and ensure you can do a smooth demo. Poor quality demos can affect your grade.
      • Don’t waste time repeating things the target audience already knows. e.g. no need to say things like "We are students from NUS, SoC".
      • Bring sufficient amount of sample data and know how to load them to the system. You should not plan to type all the sample data during the demo itself. On the other hand, trying to demo a product using just 1-2 sample data creates a bad impression too.
      • Plan the demo to be in sync with the impression you want to create. For example, if you are trying to convince that the product is easy to use, show the easiest way to perform a task before you show the full command with all the bells and whistles.
      • Limit the demo to CLI inputs only. There is no point explaining GUI inputs because they don't earn marks.

    Outcomes

    🅿️ Project

    W12.1 Can build a 1-2 KLoC program

    Complete the project as specified.

    Tutorial 12


    For W11.3b Can explain the costs and benefits of reuse
    Details of the LO

    Implementation → Reuse → Introduction →

    When

    While you may be tempted to use many libraries/frameworks/platform that seem to crop up on a regular basis and promise to bring great benefits, note that there are costs associated with reuse. Here are some:

    • The reused code may be an overkill (think using a sledgehammer to crack a nut) increasing the size of, or/and degrading the performance of, your software.
    • The reused software may not be mature/stable enough to be used in an important product. That means the software can change drastically and rapidly, possibly in ways that break your software.
    • Non-mature software has the risk of dying off as fast as they emerged, leaving you with a dependency that is no longer maintained.
    • The license of the reused software (or its dependencies) restrict how you can use/develop your software.
    • The reused software might have bugs, missing features, or security vulnerabilities that are important to your product but not so important to the maintainers of that software, which means those flaws will not get fixed as fast as you need them to.
    • Malicious code can sneak into your product via compromised dependencies.

    One of your teammates is proposing to use a recently-released “cool” UI framework for your class project. List the pros and cons of this idea.

    Pros

    • The potential to create a much better product by reusing the framework.
    • Learning a new framework is good for the future job prospects.

    Cons

    • Learning curve may be steep.
    • May not be stable (it was recently released).
    • May not allow us to do exactly what we want. While frameworks allow customization, such customization can be limited.
    • Performance penalties.
    • Might interfere with learning objectives of the module.

    Note that having more cons does not mean we should not use this framework. Further investigation is required before we can make a final decision.



    Evidence:

    Have reused software. Can explain general costs and benefits of the code you have reused.

    For W11.3c Can explain APIs
    Details of the LO

    Implementation → Reuse → APIs →

    What

    An Application Programming Interface (API) specifies the interface through which other programs can interact with a software component. It is a contract between the component and its clients.

    A class has an API (e.g., API of the Java String class, API of the Python str class) which is a collection of public methods that you can invoke to make use of the class.

    The GitHub API is a collection of Web request formats GitHub server accepts and the corresponding responses. We can write a program that interacts with GitHub through that API.

    When developing large systems, if you define the API of each components early, the development team can develop the components in parallel  because the future behavior of the other components are now more predictable.

    Choose the correct statements

    • a. A software component can have an API.
    • b. Any method of a class is part of its API.
    • c. Private methods of a class are not part of its API.
    • d. The API forms the contract between the component developer and the component user.
    • e. Sequence diagrams can be used to show how components interact with each other via APIs.

    (a) (c) (d) (e)

    Explanation: (b) is incorrect because private methods cannot be a part of the API

    Defining component APIs early is useful for developing components in parallel.

    True

    Explanation: Yes, once we know the precise behavior expected of each component, we can start developing them in parallel.



    Evidence:

    Know the API of the AddressBook component you are in charge of and the APIs of the other components your component depends on.

    For W11.3d Can explain libraries
    Details of the LO

    Implementation → Reuse → Libraries →

    What

    A library is a collection of modular code that is general and can be used by other programs.

    Java classes you get with the JDK (such as String, ArrayList, HashMap, etc.) are library classes that are provided in the default Java distribution.

    Natty is a Java library that can be used for parsing strings that represent dates e.g. The 31st of April in the year 2008

    built-in modules you get with Python (such as csv, random, sys, etc.) are libraries that are provided in the default Python distribution. Classes such as list, str, dict are built-in library classes that you get with Python.

    Colorama is a Python library that can be used for colorizing text in a CLI.



    Evidence:

    Identify libraries used (or potentially usable) in your project.

    For W11.3e Can make use of a library
    Details of the LO

    Implementation → Reuse → Libraries →

    How

    These are the typical steps required to use a library.

    1. Read the documentation to confirm that its functionality fits your needs
    2. Check the license to confirm that it allows reuse in the way you plan to reuse it. For example, some libraries might allow non-commercial use only.
    3. Download the library and make it accessible to your project. Alternatively, you can configure your dependency management tool to do it for you.
    4. Call the library API from your code where you need to use the library functionality.


    Evidence:

    Have used libraries (or reused code from elsewhere) in your project.

    For W11.3f Can explain frameworks
    Details of the LO

    Implementation → Reuse → Frameworks →

    What

    The overall structure and execution flow of a specific category of software systems can be very similar. The similarity is an opportunity to reuse at a high scale.

    Running example:

    IDEs for different programming languages are similar in how they support editing code, organizing project files, debugging, etc.

    A software framework is a reusable implementation of a software (or part thereof) providing generic functionality that can be selectively customized to produce a specific application.

    Running example:

    Eclipse is an IDE framework that can be used to create IDEs for different programming languages.

    Some frameworks provide a complete implementation of a default behavior which makes them immediately usable.

    Running example:

    Eclipse is a fully functional Java IDE out-of-the-box.

    A framework facilitates the adaptation and customization of some desired functionality.

    Running example:

    Eclipse plugin system can be used to create an IDE for different programming languages while reusing most of the existing IDE features of Eclipse. E.g. https://marketplace.eclipse.org/content/pydev-python-ide-eclipse

    Some frameworks cover only a specific components or an aspect.

    JavaFx a framework for creating Java GUIs. TkInter is a GUI framework for Python.

    More examples of frameworks

    • Frameworks for Web-based applications: Drupal(PHP), Django(Python), Ruby on Rails (Ruby), Spring (Java)
    • Frameworks for testing: JUnit (Java), unittest (Python), Jest (Java Script)


    Evidence:

    Identify frameworks used (or potentially usable) in your project.

    For W11.3g Can differentiate between frameworks and libraries
    Details of the LO

    Implementation → Reuse → Frameworks →

    Frameworks vs Libraries

    Although both frameworks and libraries are reuse mechanisms, there are notable differences:

    • Libraries are meant to be used ‘as is’ while frameworks are meant to be customized/extended.  e.g., writing plugins for Eclipse so that it can be used as an IDE for different languages (C++, PHP, etc.), adding modules and themes to Drupal, and adding test cases to JUnit.

    • Your code calls the library code while the framework code calls your code. Frameworks use a technique called inversion of control, aka the “Hollywood principle” (i.e. don’t call us, we’ll call you!). That is, you write code that will be called by the framework,  e.g. writing test methods that will be called by the JUnit framework. In the case of libraries, your code calls libraries.

    Choose correct statements about software frameworks.

    • a. They follow the hollywood principle, otherwise known as ‘inversion of control’
    • b. They come with full or partial implementation.
    • c. They are more concrete than patterns or principles.
    • d. They are often configurable.
    • e. They are reuse mechanisms.
    • f. They are similar to reusable libraries but bigger.

    (a)(b)(c)(d)(e)(f)

    Explanation: While both libraries and frameworks are reuse mechanisms, and both more concrete than principles and patterns, libraries differ from frameworks in some key ways. One of them is the ‘inversion of control’ used by frameworks but not libraries. Furthermore, frameworks do not have to be bigger than libraries all the time.

    Which one of these are frameworks ?

    (a)(b)(c)(d)

    Explanation: These are frameworks.



    Evidence:

    Explain the difference between libraries and frameworks using frameworks and libraries in your project as examples.

    For W11.3h Can explain platforms
    Details of the LO

    Implementation → Reuse → Platforms →

    What

    A platform provides a runtime environment for applications. A platform is often bundled with various libraries, tools, frameworks, and technologies in addition to a runtime environment but the defining characteristic of a software platform is the presence of a runtime environment.

    Technically, an operating system can be called a platform. For example, Windows PC is a platform for desktop applications while iOS is a platform for mobile apps.

    Two well-known examples of platforms are JavaEE and .NET, both of which sit above Operating systems layer, and are used to develop enterprise applications. Infrastructure services such as connection pooling, load balancing, remote code execution, transaction management, authentication, security, messaging etc. are done similarly in most enterprise applications. Both JavaEE and .NET provide these services to applications in a customizable way without developers having to implement them from scratch every time.

    • JavaEE (Java Enterprise Edition) is both a framework and a platform for writing enterprise applications. The runtime used by the JavaEE applications is the JVM (Java Virtual Machine) that can run on different Operating Systems.
    • .NET is a similar platform and a framework. Its runtime is called CLR (Common Language Runtime) and usually used on Windows machines.

    Enterprise Application: ‘enterprise applications’ means software applications used at organizations level and therefore has to meet much higher demands (such as in scalability, security, performance, and robustness) than software meant for individual use.



    Evidence:

    Identify platforms used (or potentially usable) in your project.

    For W11.5a Can explain unit testing
    Details of the LO

    Quality Assurance → Testing → Unit Testing →

    What

    Unit testing : testing individual units (methods, classes, subsystems, ...) to ensure each piece works correctly.

    In OOP code, it is common to write one or more unit tests for each public method of a class.

    Here are the code skeletons for a Foo class containing two methods and a FooTest class that contains unit tests for those two methods.

    class Foo{
        String read(){
            //...
        }
        
        void write(String input){
            //...
        }
        
    }
    
    class FooTest{
        
        @Test
        void read(){
            //a unit test for Foo#read() method
        }
        
        @Test
        void write_emptyInput_exceptionThrown(){
            //a unit tests for Foo#write(String) method
        }  
        
        @Test
        void write_normalInput_writtenCorrectly(){
            //another unit tests for Foo#write(String) method
        }
    }
    
    import unittest
    
    class Foo:
      def read(self):
          # ...
      
      def write(self, input):
          # ...
    
    
    class FooTest(unittest.TestCase):
      
      def test_read(sefl):
          # a unit test for read() method
      
      def test_write_emptyIntput_ignored(self):
          # a unit tests for write(string) method
      
      def test_write_normalInput_writtenCorrectly(self):
          # another unit tests for write(string) method
    

    Side readings:



    Evidence:

    Identify some unit tests in AddressBook-Level4 (or your own project).

    For W11.5b Can use stubs to isolate an SUT from its dependencies
    Details of the LO

    Quality Assurance → Testing → Unit Testing →

    Stubs

    A proper unit test requires the unit to be tested in isolation so that bugs in the dependencies cannot influence the test  i.e. bugs outside of the unit should not affect the unit tests.

    If a Logic class depends on a Storage class, unit testing the Logic class requires isolating the Logic class from the Storage class.

    Stubs can isolate the SUT from its dependencies.

    Stub: A stub has the same interface as the component it replaces, but its implementation is so simple that it is unlikely to have any bugs. It mimics the responses of the component, but only for the a limited set of predetermined inputs. That is, it does not know how to respond to any other inputs. Typically, these mimicked responses are hard-coded in the stub rather than computed or retrieved from elsewhere, e.g. from a database.

    Consider the code below:

    class Logic {
        Storage s;
    
        Logic(Storage s) {
            this.s = s;
        }
    
        String getName(int index) {
            return "Name: " + s.getName(index);
        }
    }
    
    interface Storage {
        String getName(int index);
    }
    
    class DatabaseStorage implements Storage {
    
        @Override
        public String getName(int index) {
            return readValueFromDatabase(index);
        }
    
        private String readValueFromDatabase(int index) {
            // retrieve name from the database
        }
    }
    

    Normally, you would use the Logic class as follows (not how the Logic object depends on a DatabaseStorage object to perform the getName() operation):

    Logic logic = new Logic(new DatabaseStorage());
    String name = logic.getName(23);
    

    You can test it like this:

    @Test
    void getName() {
        Logic logic = new Logic(new DatabaseStorage());
        assertEquals("Name: John", logic.getName(5));
    }
    

    However, this logic object being tested is making use of a DataBaseStorage object which means a bug in the DatabaseStorage class can affect the test. Therefore, this test is not testing Logic in isolation from its dependencies and hence it is not a pure unit test.

    Here is a stub class you can use in place of DatabaseStorage:

    class StorageStub implements Storage {
    
        @Override
        public String getName(int index) {
            if(index == 5) {
                return "Adam";
            } else {
                throw new UnsupportedOperationException();
            }
        }
    }
    

    Note how the stub has the same interface as the real dependency, is so simple that it is unlikely to contain bugs, and is pre-configured to respond with a hard-coded response, presumably, the correct response DatabaseStorage is expected to return for the given test input.

    Here is how you can use the stub to write a unit test. This test is not affected by any bugs in the DatabaseStorage class and hence is a pure unit test.

    @Test
    void getName() {
        Logic logic = new Logic(new StorageStub());
        assertEquals("Name: Adam", logic.getName(5));
    }
    

    In addition to Stubs, there are other type of replacements you can use during testing. E.g. Mocks, Fakes, Dummies, Spies.

    • Mocks Aren't Stubs by Martin Fowler -- An in-depth article about how Stubs differ from other types of test helpers.

    Stubs help us to test a component in isolation from its dependencies.

    True



    Evidence:

    Identify some tests in AddressBook-Level4,

    1. that can be made pure unit tests by introducing stubs
    2. that are using stubs to isolate the SUT from its dependencies
    For W11.5c Can explain integration testing
    Details of the LO

    Quality Assurance → Testing → Integration Testing →

    What

    Integration testing : testing whether different parts of the software work together (i.e. integrates) as expected. Integration tests aim to discover bugs in the 'glue code' related to how components interact with each other. These bugs are often the result of misunderstanding of what the parts are supposed to do vs what the parts are actually doing.

    Suppose a class Car users classes Engine and Wheel. If the Car class assumed a Wheel can support 200 mph speed but the actual Wheel can only support 150 mph, it is the integration test that is supposed to uncover this discrepancy.



    Evidence:

    Explain the difference between unit tests and integration tests.

    For W11.5d Can use integration testing
    Details of the LO

    Quality Assurance → Testing → Integration Testing →

    How

    Integration testing is not simply a repetition of the unit test cases but run using the actual dependencies (instead of the stubs used in unit testing). Instead, integration tests are additional test cases that focus on the interactions between the parts.

    Suppose a class Car uses classes Engine and Wheel. Here is how you would go about doing pure integration tests:

    a) First, unit test Engine and Wheel.
    b) Next, unit test Car in isolation of Engine and Wheel, using stubs for Engine and Wheel.
    c) After that, do an integration test for Car using it together with the Engine and Wheel classes to ensure the Car integrates properly with the Engine and the Wheel.

    In practice, developers often use a hybrid of unit+integration tests to minimize the need for stubs.

    Here's how a hybrid unit+integration approach could be applied to the same example used above:

    (a) First, unit test Engine and Wheel.
    (b) Next, unit test Car in isolation of Engine and Wheel, using stubs for Engine and Wheel.
    (c) After that, do an integration test for Car using it together with the Engine and Wheel classes to ensure the Car integrates properly with the Engine and the Wheel. This step should include test cases that are meant to test the unit Car (i.e. test cases used in the step (b) of the example above) as well as test cases that are meant to test the integration of Car with Wheel and Engine (i.e. pure integration test cases used of the step (c) in the example above).

    💡 Note that you no longer need stubs for Engine and Wheel. The downside is that Car is never tested in isolation of its dependencies. Given that its dependencies are already unit tested, the risk of bugs in Engine and Wheel affecting the testing of Car can be considered minimal.



    Evidence:

    Use tests from AddressBook-Level4 to illustrate the difference between unit testings and integration testing. Hint: good examples seedu.address.storage.StorageManagerTest,seedu.address.logic.commands.AddCommandTest,seedu.address.logic.commands.AddCommandIntegrationTest

    For W11.5e Can explain system testing
    Details of the LO

    Quality Assurance → Testing → System Testing →

    What

    System testing: take the whole system and test it against the system specification.

    System testing is typically done by a testing team (also called a QA team).

    System test cases are based on the specified external behavior of the system. Sometimes, system tests go beyond the bounds defined in the specification. This is useful when testing that the system fails 'gracefully' having pushed beyond its limits.

    Suppose the SUT is a browser supposedly capable of handling web pages containing up to 5000 characters. Given below is a test case to test if the SUT fails gracefully if pushed beyond its limits.

    Test case: load a web page that is too big
    * Input: load a web page containing more than 5000 characters. 
    * Expected behavior: abort the loading of the page and show a meaningful error message. 
    

    This test case would fail if the browser attempted to load the large file anyway and crashed.

    System testing includes testing against non-functional requirements too. Here are some examples.

    • Performance testing – to ensure the system responds quickly.
    • Load testing (also called stress testing or scalability testing) – to ensure the system can work under heavy load.
    • Security testing – to test how secure the system is.
    • Compatibility testing, interoperability testing – to check whether the system can work with other systems.
    • Usability testing – to test how easy it is to use the system.
    • Portability testing – to test whether the system works on different platforms.


    Evidence:

    Explain what system tests are. Give examples from your own project.

    For W11.5f Can explain acceptance testing
    Details of the LO

    Quality Assurance → Testing → Acceptance Testing →

    What

    Acceptance testing (aka User Acceptance Testing (UAT)): test the delivered system to ensure it meets the user requirements.

    Acceptance tests give an assurance to the customer that the system does what it is intended to do. Acceptance test cases are often defined at the beginning of the project, usually based on the use case specification. Successful completion of UAT is often a prerequisite to the project sign-off.



    Evidence:

    Explain what acceptance tests are. Explain how acceptance testing of your project will be done.

    For W11.5g Can explain the differences between system testing and acceptance testing
    Details of the LO

    Quality Assurance → Testing → Acceptance Testing →

    Acceptance vs System Testing

    Acceptance testing comes after system testing. Similar to system testing, acceptance testing involves testing the whole system.

    Some differences between system testing and acceptance testing:

    System Testing Acceptance Testing
    Done against the system specification Done against the requirements specification
    Done by testers of the project team Done by a team that represents the customer
    Done on the development environment or a test bed Done on the deployment site or on a close simulation of the deployment site
    Both negative and positive test cases More focus on positive test cases

    Note: negative test cases: cases where the SUT is not expected to work normally e.g. incorrect inputs; positive test cases: cases where the SUT is expected to work normally

    Requirement Specification vs System Specification

    The requirement specification need not be the same as the system specification. Some example differences:

    Requirements Specification System Specification
    limited to how the system behaves in normal working conditions can also include details on how it will fail gracefully when pushed beyond limits, how to recover, etc. specification
    written in terms of problems that need to be solved (e.g. provide a method to locate an email quickly) written in terms of how the system solve those problems (e.g. explain the email search feature)
    specifies the interface available for intended end-users could contain additional APIs not available for end-users (for the use of developers/testers)

    However, in many cases one document serves as both a requirement specification and a system specification.

    Passing system tests does not necessarily mean passing acceptance testing. Some examples:

    • The system might work on the testbed environments but might not work the same way in the deployment environment, due to subtle differences between the two environments.
    • The system might conform to the system specification but could fail to solve the problem it was supposed to solve for the user, due to flaws in the system design.

    Choose the correct statements about system testing and acceptance testing.

    • a. Both system testing and acceptance testing typically involve the whole system.
    • b. System testing is typically more extensive than acceptance testing.
    • c. System testing can include testing for non-functional qualities.
    • d. Acceptance testing typically has more user involvement than system testing.
    • e. In smaller projects, the developers may do system testing as well, in addition to developer testing.
    • f. If system testing is adequately done, we need not do acceptance testing.

    (a)(b)(c)(d)(e)(f)

    Explanation:

    (b) is correct because system testing can aim to cover all specified behaviors and can even go beyond the system specification. Therefore, system testing is typically more extensive than acceptance testing.

    (f) is incorrect because it is possible for a system to pass system tests but fail acceptance tests.



    Evidence:

    Explain the difference between system tests and acceptance tests. Explain why both are needed.

    For W11.5h Can explain alpha and beta testing
    Details of the LO

    Quality Assurance → Testing → Alpha/Beta Testing →

    What

    Alpha testing is performed by the users, under controlled conditions set by the software development team.

    Beta testing is performed by a selected subset of target users of the system in their natural work setting.

    An open beta release is the release of not-yet-production-quality-but-almost-there software to the general population. For example, Google’s Gmail was in 'beta' for many years before the label was finally removed.



    Evidence:

    Explain alpha and beta testing. How can you do alpha and beta testing in your project?

    For W11.6b Can use intermediate features of JUnit
    Details of the LO

    Tools → JUnit →

    JUnit: Intermediate

    Some intermediate JUnit techniques that may be useful:



    Evidence:

    Use the mentioned features in the project.

    For W11.7b Can explain validation and verification
    Details of the LO

    Quality Assurance → Quality Assurance → Introduction →

    Validation vs Verification

    Quality Assurance = Validation + Verification

    QA involves checking two aspects:

    1. Validation: are we building the right system i.e., are the requirements correct?
    2. Verification: are we building the system right i.e., are the requirements implemented correctly?

    Whether something belongs under validation or verification is not that important. What is more important is both are done, instead of limiting to verification (i.e., remember that the requirements can be wrong too).

    Choose the correct statements about validation and verification.

    • a. Validation: Are we building the right product?, Verification: Are we building the product right?
    • b. It is very important to clearly distinguish between validation and verification.
    • c. The important thing about validation and verification is to remember to pay adequate attention to both.
    • d. Developer-testing is more about verification than validation.
    • e. QA covers both validation and verification.
    • f. A system crash is more likely to be a verification failure than a validation failure.

    (a)(b)(c)(d)(e)(f)

    Explanation:

    Whether something belongs under validation or verification is not that important. What is more important is that we do both.

    Developer testing is more about bugs in code, rather than bugs in the requirements.

    In QA, system testing is more about verification (does the system follow the specification?) and acceptance testings is more about validation (does the system solve the user’s problem?).

    A system crash is more likely to be a bug in the code, not in the requirements.



    Evidence:

    Explain validations and verification with concrete examples from the project.

    For W11.7c Can explain code reviews
    Details of the LO

    Quality Assurance → Quality Assurance → Code Reviews →

    What

    Code review is the systematic examination code with the intention of finding where the code can be improved.

    Reviews can be done in various forms. Some examples below:

    • In pair programming

      • As pair programming involves two programmers working on the same code at the same time, there is an implicit review of the code by the other member of the pair.

    Pair Programming:

    Pair programming is an agile software development technique in which two programmers work together at one workstation. One, the driver, writes code while the other, the observer or navigator, reviews each line of code as it is typed in. The two programmers switch roles frequently. [source: Wikipedia]


    [image credit: Wikipedia]

    A good introduction to pair programming:

    • Pull Request reviews

      • Project Management Platforms such as GitHub and BitBucket allows the new code to be proposed as Pull Requests and provides the ability for others to review the code in the PR.
    • Formal inspections

      • Inspections involve a group of people systematically examining a project artifacts to discover defects. Members of the inspection team play various roles during the process, such as:

        • the author - the creator of the artifact
        • the moderator - the planner and executor of the inspection meeting
        • the secretary - the recorder of the findings of the inspection
        • the inspector/reviewer - the one who inspects/reviews the artifact.

    Advantages of code reviews over testing:

    • It can detect functionality defects as well as other problems such as coding standard violations.
    • Can verify non-code artifacts and incomplete code
    • Do not require test drivers or stubs.

    Disadvantages:

    • It is a manual process and therefore, error prone.


    Evidence:

    Review PRs in the project.

    For W11.7d Can explain static analysis
    Details of the LO

    Quality Assurance → Quality Assurance → Static Analysis →

    What

    Static analysis: Static analysis is the analysis of code without actually executing the code.

    Static analysis of code can find useful information such unused variables, unhandled exceptions, style errors, and statistics. Most modern IDEs come with some inbuilt static analysis capabilities. For example, an IDE can highlight unused variables as you type the code into the editor.

    Higher-end static analyzer tools can perform for more complex analysis such as locating potential bugs, memory leaks, inefficient code structures etc.

    Some example static analyzer for Java:

    Linters are a subset of static analyzers that specifically aim to locate areas where the code can be made 'cleaner'.



    Evidence:

    Explain how static analysis is used in the project.

    For W11.8 Package the app as a JAR file

    Extract from the Admin Info page:

    Week 11

    • Enhance your project (TaskManager or otherwise) as explained in the exercise below:

    This exercise continues from the TaskManager Level 1-11 exercises quoted above.

    Package your TaskManager as a JAR file.

    Lecture 12

    no webcast

    Practice exam

    • Seating row assignment is here

    • Answers are given below. If you have any doubts about any of the question, please post in our forum to discuss.
      Q: A
      A1: A
      A2: A
      A3: A
      A4: E
      A5: E
      A6: A
      A7: E
      A8: A
      A9: E
      A10: A
      A11: A
      A12: E
      A13: E
      A14: E
      A15: A
      A16: A
      A17: E
      A18: E
      A19: E
      A20: E
      A21: A
      A22: E
      A23: A
      A24: A
      A25: E
      A26: A
      A27: E
      A28: A
      A29: E
      A30: A
      A31: A
      A32: A
      A33: E
      A34: E
      A35: A
      A36: A
      A37: A
      A38: E
      A39: A
      A40: C (This is a nonsense question, put there to give you a chance to use the C option. No such deliberately incorrect questions in the actual exam paper though)
      A41: E
      A42: A
      A43: A
      A44: A
      A45: A
      A46: A
      A47: A
      A48: A
      A49: E
      A50: E

    Talk on getting ready for internships