Adventures in Software Craftsmanship

Day 1 - Morning

  • Intro and Philosophy
  • Core Principles
  • Clean code conventions

Day 1

  • Facilitator and Class Intro
  • Poll
  • Intro to SC
  • Code reading competition
  • Intro to SC
  • Sandwich activity
  • LUNCH
  • Design principles
  • Interface design activity
  • Code conventions
  • Code smell activity
  • Unit testing and TDD
  • TDD class activity
  • Mocking test objects

Day 2

  • Intro to design patterns
  • Design pattern live refactoring
  • Design patterns
  • LUNCH
  • Design pattern group activity
  • Ore Trader introduction (and maybe planning)

Day 3

  • Project/Environment setup
  • GitHub overview
    • Clone
    • Diff before commit!!
    • Commit messages
    • Branching
    • Push vs. Pull Request
    • Team collaboration and best practices
    • (Git practice games)
  • Remaining design planning
  • Design presentations
  • Ore Trader implementation (stress TDD)

Day 4

  • Ore Trader
  • LUNCH
  • Live code reviews
  • Ore Trader improvements
  • Final presentations

Introduction

Reid Harrison

August '13

Digital STS

Unified Presentation Framework

Scott Franks

February '14

Retail Bank

Rage Against the Machine - iOS

Rob Wirthman

February '14

Digital STS

DTS Access Controls

bit.ly/1Fsti0L

http://pollev.com/reidharrison715

Catie Edwards

July '14

Tech Ops

Private Cloud

What is "Software Craftsmanship?"

A craftsman or artisan is a skilled manual worker who makes items that may be functional or strictly decorative.

How is software a craft?

How is software different from typical crafts?

What things make programmers artisans?

Why is it important to improve in your craft?

What are important qualities of a skilled craftsman?

How can you improve your crafting skills?

The Art of Software

Writing code is similar to painting a picture. A programmer who writes clean code is an artist who can guide a blank screen through a series of transformations until it is an elegantly coded system.

  • ​Easy to recognize a good painting
  • Hard to paint a good picture


Programmers are authors – responsible for communicating well with their readers.

  • You write for readers who will judge your effort and skill
  • The ratio spent reading vs. writing code is usually at least 10:1


Theme of elegance. 

“If at the end, it’s not beautiful, I know it’s not a good solution” “Good developers remove code, not write more.”

The Science of Software

-Design student: storyboard, plan, design. By the time they get to the final solution, it’s already planned. “The code should write itself” after it’s planned. Elegance means you follow a DISCIPLINED process. Compare to other development process. J.K. Rowling’s HP universe. TDD: writing the test caused me to solve the problem rather than doodling in the code. Donald Knuth: first solve the problem and then write the code to complete the solution. Break the bad habit of “I don’t know what to do so I’ll just write the code and see what happens” Engineers: “Let’s build a bridge and see where it goes.” Experiment with ideas and designs, just not in the final code. 

-Don’t want to go back and do things a second time because you made a mistake. Shouldn’t mess it up by not doing it write the first time. Failures from taking calculated risk is good!

-Outside->in design. As we scale up, design becomes even more important because of complications and communication.

-Military procedures become extremely important when you need them.

-Emphasize this is not a software design process, but an implementation of the design process that works everywhere else manifested in software. Very similar to engineering. Get engineers to say “yeah we did this stuff in engineering”

Traits

  • Inquisitive
  • Early adopter
  • Critical thinker
  • Realistic
  • Jack of all trades

of a software craftsman

Virtues

  • Patient
  • Brave
  • Independent
  • Quality focused
  • Balanced

Rework (tie into Capital One)

From Journeyman to Master

Care about your craft

Think about your work

Continuously make small improvements

As a student:

  • Solve it
  • Test it and fix it
  • Turn it in
  • Forget about it

As a professional:

  • Solve it
  • Make it easy to understand
  • Make it easy to use
  • Make it easy to change
  • Test it and fix it
  • Commit it
  • Release it
  • Revisit it

Changed

throw new EPFSystemException("Structure exceeds two levels " + objEntry + " " + json);
if (l2Temp != null && l2Temp.isValueNode()) {
  logger.debug("Is nested value node {} stack id {}", objEntry, stackId);
  buildAndAddEvtDetail(event, objEntry, l2Temp.asText(), stackId);
}
else {
  throw new EPFSystemException("Structure exceeds two levels " + objEntry + " " + json);
}
for (int x = 0; x < anode.size(); x++) {
  Iterator<String> level2 = anode.get(x).fieldNames();
  while (level2.hasNext()) {
    String objEntry = level2.next();
    logger.debug("Processing nested object {} stack id {}", objEntry, stackId);
    JsonNode l2Temp = anode.get(objEntry);
    if (l2Temp != null && l2Temp.isValueNode()) {
      logger.debug("Is nested value node {} stack id {}", objEntry, stackId);
      buildAndAddEvtDetail(event, objEntry, l2Temp.asText(), stackId);
    }
    else {
      throw new EPFSystemException("Structure exceeds two levels " + objEntry + " " + json);
    }
  }
  stackId++;
}
if (temp.isValueNode()) {
  logger.debug("Found value node, building detail {}", fname);
  buildAndAddEvtDetail(event, fname, temp.asText(), 0);
}
else if (temp.isArray()) {
  logger.debug("Found array, building detail {}", fname);
  ArrayNode anode = (ArrayNode) temp;
  if (anode.size() > 0) {
    JsonNode obj = findNonNullObject(anode);
    if (obj == null || obj.isValueNode()) {
      logger.debug("Found Array of value nodes {} size {}", fname, anode.size());
      int stackId = 1;
      for (int x = 0; x < anode.size(); x++) {
        if (anode.get(x) != null) {
          buildAndAddEvtDetail(event, fname, anode.get(x).asText(), stackId);
        }
        stackId++;
      }
    }
    else if (obj.isObject()) {
      logger.debug("Found Array of objects {} size {}", fname, anode.size());
      int stackId = 1;
      for (int x = 0; x < anode.size(); x++) {
        Iterator<String> level2 = anode.get(x).fieldNames();
        while (level2.hasNext()) {
          String objEntry = level2.next();
          logger.debug("Processing nested object {} stack id {}", objEntry, stackId);
          JsonNode l2Temp = anode.get(objEntry);
          if (l2Temp != null && l2Temp.isValueNode()) {
            logger.debug("Is nested value node {} stack id {}", objEntry, stackId);
            buildAndAddEvtDetail(event, objEntry, l2Temp.asText(), stackId);
          }
          else {
            throw new EPFSystemException("Structure exceeds two levels " + objEntry + " " + json);
          }
        }
        stackId++;
      }
    }
    else {
      throw new EPFSystemException("error - found unexpected object type found " + fname + " " + json);
    }
  }
}
else if (temp.isObject()) {
  logger.debug("Found object, building detail {}", fname);
  Iterator<String> level1 = temp.fieldNames();
  while (level1.hasNext()) {
    String objEntry = level1.next();
    logger.debug("Processing object {}", objEntry);
    JsonNode l2Temp = temp.get(objEntry);
    if (l2Temp != null && l2Temp.isValueNode()) {
      logger.debug("Is a value node {}", objEntry);
      buildAndAddEvtDetail(event, objEntry, l2Temp.asText(), 0);
    }
    else {
      throw new EPFSystemException("error - found unexpected object type found " + objEntry + " " + json);
    }
  }
}

The Perils of Messy Code

Mess builds, productivity of team decreases

Under pressure, the team makes more messes

Entropy - The amount of disorder in a system. When disorder increases in software, it is called "code rot."

The only way to develop quickly and meet deadlines is to keep the code as clean as possible at all times.

http://xkcd.com/844/

public void fireLoadSummaryEventAction(ActionRequest request, ActionResponse response) {
    ApplicationObject appObject = null;
    String token = null;
    EPFContext context = EPFContextContainer.getcurrentContext().get();
    String correlationID = context.getCorrelationId();
    try {
        try {
            //retrieve token
            HttpServletRequest httpServletReq = (HttpServletRequest) request.getAttribute("javax.servlet.request");
            HttpSession session = httpServletReq.getSession();
            token = (String)session.getAttribute("eosToken");
            //retrieve composite object and store in cache
            appObject = creditcardappdataserviceclient.retrieveApplicationData(token);			
            //store composite object in cache
            UserContext userContext = cache.getCachedUserContext(correlationID, appObject);	
        } catch (JaxWsSoapFaultException sfe) {
            logger.error("SOAP Fault on composite object retreival");
            appObject = new ApplicationObject();
            //set statCd to soap fault, for data retrieval audit logging
            setStatCd(appObject,STATUS_CODE_RTM_SOAP_FAULT);
            //audit data retrieval status		
            auditLogUtil.auditEOSEvent(token, auditLogUtil.AUDIT_EVENT_APP_DATA_RETRIEVAL_NAME, appObject, auditLogUtil.AUDIT_EVENT_STATUS_SOAP_FAULT);
            //set statCd to "data not retrieved", for soft decline audit logging
            setStatCd(appObject,STATUS_CODE_DATA_RETRIEVAL_FAILED);
            throw new EPFSystemException("SOAP Fault", sfe);
        } catch (Exception e) {
            logger.error("Error retrieving or caching composite object. " + e.getMessage());
            if (appObject == null ) {
                appObject = new ApplicationObject();
            }
            //set statCd to general error, for data retrieval audit logging
            setStatCd(appObject,STATUS_CODE_GENERAL_DATA_RETRIEVAL_ERROR);
            //audit data retrieval status
            auditLogUtil.auditEOSEvent(token, auditLogUtil.AUDIT_EVENT_APP_DATA_RETRIEVAL_NAME, appObject, auditLogUtil.AUDIT_EVENT_STATUS_ERROR);
            //set statCd to "data not retrieved", for soft decline audit logging
            setStatCd(appObject,STATUS_CODE_DATA_RETRIEVAL_FAILED);
            throw new EPFBusinessException(new EPFMessage(e.getMessage()));
        }
        //audit data retrieval status
        auditLogUtil.auditEOSEvent(token, auditLogUtil.AUDIT_EVENT_APP_DATA_RETRIEVAL_NAME, appObject, null);
        //check for an error/warning in stat code
        if (findStatusError(appObject)) {
            String errorCode = getStatCode(appObject);
            String errorMsg = "A status of error or warning was returned by RTM.  RTM Status code:" + errorCode; 
            logger.warn(errorMsg);
            //set statCd to "data not retrieved", for soft decline audit logging
            setStatCd(appObject,STATUS_CODE_DATA_RETRIEVAL_FAILED);
            throw new EPFBusinessException(new EPFMessage(errorCode));
        }
        //check if token expired
        verifyTokenNotExpired(appObject);
        //verify the disclosure can be built
        buildDisclosure(appObject);
        //update disclosure flag
        invokeDisclosureFlagUpdate(appObject, token);
        //fetch values required by DSD portlets
        EnrollmentEvent sspEvent  = getUserInfo(appObject);
        //fire JSR event to invoke DSD portlet to receive required info
        QName qname2 = new QName("http://www.capitalone.com/sspEvent","sspEvent");
        response.setEvent(qname2, sspEvent);
        //fire JSR event to invoke summary portlet controller
        QName qname = new QName("http://eos.capitalone.com","loadResultsCustomEvent");
        response.setEvent(qname, null);
        //fire WLP event for portal page change
        QName qname1 = new QName("http://eos.capitalone.com","pageChangeEvent");
        response.setEvent(qname1, null);
        response.setRenderParameter("myaction", "done");
    } catch (Exception e) {
        logger.error("Error preparing to display results", e);
        //audit soft approval (display of error page)
        auditLogUtil.auditEOSEvent(token, auditLogUtil.AUDIT_EVENT_APP_SOFT_DECLINE_NAME, appObject, auditLogUtil.AUDIT_EVENT_STATUS_NOT_PROVIDED);
        //set render param to invoke error page
        response.setRenderParameter("myaction", "errorRenderMapping");
    }
}	

What is messy code?

  • Smells bad
  • Follows anti-patterns
  • Contains spaghetti code
  • Duplication
  • Entities are too complex
  • Intent isn't clear
  • Logical flow is hard to follow
  • Unused or unmaintained
  • Inconsistent

-Messy code is a SYMPTOM of an underlying poor design or other problem

What is clean code?

  • Simple and direct
  • Reads like well-written prose
  • Never obscures the designer's intent
  • Full of efficient abstractions
  • Straightforward logical flow
  • Can be read and enhanced by new developers
  • Has unit and acceptance tests
  • Provides one way to do one thing
  • Provides a clear and minimal API
  • Has minimal dependencies

-Clean code is the REWARD for elegant design, planning, and execution.

Mantras of a Software Craftsman

  • Broken windows
    • ​One broken window, left un-repaired, instills in the community a sense of abandonment.
  • Boy Scout rule
    • ​"Leave the campground cleaner than you found it."
  • Tests are your parachute
    • Keep them free of holes and obstructions.

Your Knowledge Portfolio

Your knowledge and experience are your most important professional assets.

Unfortunately, they're expiring assets.

  • Invest regularly
  • Diversify
  • Manage risk
  • Buy low, sell high
  • Review and rebalance

Expand your knowledge portfolio regularly; not only with technical expertise but also Capital One expertise.

SOLID Design Principles

  • Single Responsibility Principle (SRP)

  • Open/Closed Principle (OCP)

  • Liskov Substitution Principle (LSP)

  • Interface Segregation Principle (ISP)

  • Dependency Inversion Principle (DIP)

public Map<String, UPFAppFeature> getAllFeature(DomainProfile domainInfo) {
    Map<String, UPFAppFeature> upfAppFeatureMap = new Hashtable<String, UPFAppFeature>();
    if (domainInfo.getSystemOfRecord().equalsIgnoreCase(FeatureToggleConstants.DB_SYSTEM_STORAGE)) {
        try {
            readItFromDB(domainInfo, upfAppFeatureMap);
        }
        catch (RuntimeException dbError) {
            logger.error("Error reading from Database, the provided details were: App Name: " + domainInfo.getAppName()
                + " Domain Name:" + domainInfo.getDomainIndentifier()
                + ". Will try reading it from backup source (file system)", dbError);
            readItFromFileSystem(domainInfo, upfAppFeatureMap);
        }
    }
    else if (domainInfo.getSystemOfRecord().equalsIgnoreCase(FeatureToggleConstants.FILE_SYSTEM_STORAGE)) {
        readItFromFileSystem(domainInfo, upfAppFeatureMap);
    }
    else {
        logger.error("ERROR fetching features from system of record, no matching system of record found: ",
            domainInfo.getSystemOfRecord());
        return null;
    }

    return upfAppFeatureMap;
}
private void readItFromDB(DomainProfile domainInfo, Map<String, UPFAppFeature> upfAppFeatureMap) {
    logger.debug("Fetching features data from DB system, the system of record value is: "
        + domainInfo.getSystemOfRecord() + "App Name: " + domainInfo.getAppName() + " Domain Name:"
        + domainInfo.getDomainIndentifier());

        Map<String, Feature> featureFromDB;

        if (isMongoDBImpl()) {
            featureFromDB = mongoDBFeatureStore.readAll(domainInfo.getAppName(), domainInfo.getDomainIndentifier());
        }
        else {
            featureFromDB = jdbcFeatureStore.readAll(domainInfo.getAppName(), domainInfo.getDomainIndentifier());
        }

        loadUPFFeaturesMap(featureFromDB, upfAppFeatureMap);

        // write it to the file system for backup activities:
        try {
            writeFeaturesOnFilesystemForBackup(featureFromDB);
        }
        catch (Exception excpetion) {
            logger.error("Could not write to file system path: " + domainInfo.getFeaturesFilePath(), excpetion);
        }
}
public void toggleFeature(DomainProfile domainProfile, String regionIdentifier, String toggle, String featureName, String featureGroup) {
    if (domainProfile.getSystemOfRecord().equalsIgnoreCase(FeatureToggleConstants.DB_SOR)) {
        if (toggle.equalsIgnoreCase("enable")) {
            if (isMongoDBImpl()) {
                mongoDBFeatureStore.enable(featureName, featureGroup, regionIdentifier);
            }
            else {
                jdbcFeatureStore.enable(featureName, featureGroup, regionIdentifier);
            }
        }
        else if (toggle.equalsIgnoreCase("disable")) {
            if (isMongoDBImpl()) {
                mongoDBFeatureStore.disable(featureName, featureGroup, regionIdentifier);
            }
            else {
                jdbcFeatureStore.disable(featureName, featureGroup, regionIdentifier);
            }
        }
        else {
            EPFMessage error = new EPFMessage(appConfiguration.getString(FeatureToggleConstants.INVALID_FLIPPING_OPTION_ERROR_CODE));
            error.setInputParams(appConfiguration.getString(FeatureToggleConstants.INVALID_FLIPPING_OPTION_MESSAGE));
            throw new RuntimeException(FeatureToggleConstants.INVALID_FLIPPING_OPTION_MESSAGE);
        }
    }
    else if (domainProfile.getSystemOfRecord().equalsIgnoreCase(FeatureToggleConstants.FS_SOR)) {
        if (toggle.equalsIgnoreCase("enable")) {
            fileSystemStore.enable(featureName);
        }
        else if (toggle.equalsIgnoreCase("disable")) {
            fileSystemStore.disable(featureName);
        }
        else {
            EPFMessage error = new EPFMessage(appConfiguration.getString(FeatureToggleConstants.INVALID_FLIPPING_OPTION_ERROR_CODE));
            error.setInputParams(appConfiguration.getString(FeatureToggleConstants.INVALID_FLIPPING_OPTION_MESSAGE));
            throw new RuntimeException(FeatureToggleConstants.INVALID_FLIPPING_OPTION_MESSAGE);
        }
    }
    else {
        throw new EPFSystemException(FeatureToggleConstants.INVALID_SYSTEM_OF_RECORD_OPT);
    }
}

Single Responsibility Principle

 

  • The single responsibility should be entirely encapsulated by its context

  • A unit of code should have only one reason to change

 

  • Keeps a class focused (high cohesion)

  • Lowers coupling

A class or module should be defined by one, and only one, responsibility.

Open/Closed Principle

 

  • Allows its behavior to be extended without modifying its source code

  • Utilizes inheritance and polymorphism

 

  • Promotes code reusability

  • Prevents unnecessary code reviews and testing

Software entities should be open for extension, but closed for modification.

Liksov Substitution Principle

Objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program.

 

Polymorphism

 

Always reference the most abstract type that provides all the required methods. Subclass implementations can then be swapped out later or multiple implementations can be accommodated by a library.

 

Code example

 

Interface Segregation Principle

Keep interfaces focused and segregated by purpose.

many client-specific interfaces are better than one general-purpose interface.

 

Also talk about interface-based design:

-Separating implementation and interfaces. Designing from the outside in. Write the interfaces first and then the implementation. Students just start writing code and then slap the interface on. TDD forces this kind of design. Interfaces are the foundation of APIs.

-Give an example of an interface and why it’s good/bad. Use this to talk about the OCP and Liskov principle. 

Dependency Inversion Principle

 

  • Separate high-level and low-level components into separate packages

  • Interfaces belong in high-level package, implementations in low-level

  • Keep consistent and developer friendly

 

  • Allows for swapping implementations

  • Promotes code encapsulation with flexibility

  1. High-level modules should not depend on low-level modules.
  2. Abstractions should not depend on details.
   public String generateMessage(StreamMessageContext ctx, Object... data) {
        int size = ((data == null) ? 1 : data.length + 1);
        
        // serialize each object
        int i = 1;
        String[] jsonEntries = new String[size];
        jsonEntries[0] = DataConversionSupport.serializeToJsonString(ctx);
        for (Object datum : data) {
            jsonEntries[i] = DataConversionSupport.serializeToJsonString(datum);
            ++i;
        }
        
        // then aggregate all the serializations together
        StringBuilder sb = new StringBuilder();
        sb.append('{').append('\"').append(ctx.getClass().getSimpleName()).append("\":").append(jsonEntries[0]); // the key
        for (int j = 0; j < i - 1; ++j) {
            sb.append(',\"').append(data[j].getClass().getSimpleName()).append("\":"); // item name
            sb.append(jsonEntries[j + 1]); // item json
        }
        sb.append('}');
        return sb.toString();
    }
    
    public String generateMessage(StreamMessageContext ctx, String[] dataKeys, Object... data) {
        int size = ((data == null) ? 1 : data.length + 1);
        
        // serialize each object
        int i = 1;
        String[] jsonEntries = new String[size];
        jsonEntries[0] = DataConversionSupport.serializeToJsonString(ctx);
        for (Object datum : data) {
            jsonEntries[i] = DataConversionSupport.serializeToJsonString(datum);
            ++i;
        }
        
        // build up keys
        String[] keys = processDataKeys(dataKeys, data);
        
        // then aggregate all the serializations together
        StringBuilder sb = new StringBuilder();
        sb.append('{').append('\"').append(ctx.getClass().getSimpleName()).append("\":").append(jsonEntries[0]); // the key
        for (int j = 0; j < i - 1; ++j) {
            sb.append(',\"').append(keys[j]).append("\":"); // item name
            sb.append(jsonEntries[j + 1]); // item json
        }
        sb.append('}');
        return sb.toString();
    }

The Evils of Duplication

  • Requirements, understanding, and environments change. Your code will need to change with it.

  • It's not a matter of if you'll remember, but when you'll forget.

DRY - Don't Repeat Yourself

  • Imposed duplication
  • Inadvertent duplication
  • Impatient duplication
  • Inter-developer duplication

Orthogonality

  • When components are highly interdependent, there is no such thing as a quick, local fix.

  • Orthogonal software provides increased productivity and decreased risk. Developers never have to worry about side-effects of making changes.

  • Orthogonal components are easier to swap meaning less dependence on a vendor.

Two or more modules are orthogonal if changes in one do not affect the other.

if (appObject.isDsclsrOnlineRequiredInd() || appObject.isDsclsrCreditScoreRequiredInd()) {
    if (appObject.isDsclsrOnlineRequiredInd()) {
        // Pull account disclosure
        setDisclosureVariableFromContent(AOD, aodVariableReplacer,
            appObject.getDecisionedOfferID(), model, evalContext);
    }
            
    if (appObject.isDsclsrCreditScoreRequiredInd() && appObject.getMinCreditScore() != null 
            && appObject.getMinCreditScore().getBureauCode()!=null) {
        // Pull credit disclosure
        if(appObject.getMinCreditScore().getBureauCode().equals("")){
            setDisclosureVariableFromContent(CSD, csdVariableReplacer,
               NO_HIT_KEY , model, evalContext);
        }else{
            setDisclosureVariableFromContent(CSD, csdVariableReplacer,
                appObject.getMinCreditScore().getBureauCode() , model, evalContext);
        }
    }
}

if (appObject.getApplicant() != null && appObject.getApplicant().get(0) != null ) { 
    userInfo.setFirstName(appObject.getApplicant().get(0).getFirstName());
    userInfo.setLastName(appObject.getApplicant().get(0).getLastName());			
    userInfo.setLangCode(appObject.getApplicant().get(0).getLangCode());
    userInfo.setSsoId(null);
    userInfo.setUserId(appObject.getApplicant().get(0).getCustomerID());
			
    if (appObject.getApplicant().get(0).getEmailAddresses() != null  &&
            appObject.getApplicant().get(0).getEmailAddresses().size() > 0) {	
        userInfo.setEmailAddress(appObject.getApplicant().get(0).getEmailAddresses().get(0));
    }	
}

Coupling and Cohesion

With tightly coupled systems:

  1. A change in one module forces changes in others
  2. Modules are harder to reuse
  3. Modules are harder to test

With low cohesion systems:

  1. Modules are complex with more operations
  2. Modules are less maintainable and harder to work with

Coupling is a measure of how closely connected two routines or modules are.

Cohesion is a measure of how strongly related each piece of functionality is.

Coupling and Cohesion have an inverse relationship

Lead with examples before introducing the concept. Show the WHY first: example of tight coupling and low cohesion and discuss the negative impact first

Law of Demeter

  1. Object A can call a method of object B but should not "reach through" B to acquire a reference to object C.
  2. A method m of class A may only invoke methods on:
    1. A itself
    2. m's parameters
    3. Objects instantiated within m
    4. A's direct instance objects
    5. A global variable, accessible by A, in the scope of m

Since objects are less dependent on the internal structure of other objects, classes can be changed without reworking their callers.

Rework (too academic)

Start with examples of breaking the law before introducing all of the rules.

Follow the Rules

-Make a distinction between rules and principles. “A good design is associated with the follow principles” Don’t need to go into a lot of detail. Rules are attempts to standardize agreement of how we follow the principles. These strats change over time. The underlying principles that warrant these rules are the same. Code standards are constantly evolving because we’re constantly trying to achieve clean code. With rules, we all agree as a group in this standard way. Breaking rules is fine, breaking principles will cost us down the line. “There’s a rule you should always tell the truth. No - the principle is to always be kind.” Suffering in a car crash example.

Naming Conventions

Intention revealing

int d; // elapsed time in days
int daysSinceCreation;

Avoid disinformation

Account[] accountList;
boolean notActive;

Make meaningful distinctions

void arrayCopy(char[] a1, char[] a2);
void arrayCopy(char[] source, char[] destination);

Pronounceable

String evtStCd, evtAudtg;

Searchable

Date date, transactionDate;
public class RequestBuilder { ...

Solution/Problem relevant

int tableUsage, loadFactor;
Node tortoise, hare;

Function Conventions

  • Small
  • Do only one thing
  • One level of abstraction per function
  • Descriptive names

 

int getStatus();
int getResponseStatusCode();
  • Minimal parameters
  • Don't pass codes
  • Don't return null
  • Verbs not nouns
int cardCount();
int getCardCount();

void newCard();
void addNewCard();

Commenting Conventions

"Programs must be written for people to read and only incidentally for machines to execute" - Hal Abelson

  • The only "comment" guaranteed to be accurate is the code its self
  • Comments have to be maintained and updated same as the code
  • The more complex the comments are, the more likely they are to be wrong or out of date

The best solution is to write easily comprehensible code.

Good Comments

Code can't explain why the program is being written, and the rationale for choosing this or that method. Code cannot discuss reasons certain alternative approaches were taken.

Comments are best used to provide contextual information that makes it easier to understand the code.

  • How the code fits into the big picture
  • Why this methodology was chosen and why others were rejected
  • Code is written to solve a problem. Describe how your code solves the problem and why it is better than the alternatives
  • Warning of consequences

Bad Comments

Single line comments are usually unnecessary and should only be used if the operation is complex.

j = j + 1; //Increment j
int a = c * 100; //convert to cents
double avg = a / n; //average cents per customer

int totalCents = totalDollars * 100;
double averageCentsPerCustomer = totalCents / customerCount;

Instead of writing comments that are designed to make code more readable, rewrite the code.

BAD

GOOD

Do not release with TODOs or commented-out code 



    /*
    * Purpose: Check the status of the composite object for errors or warnings.
    */
    private boolean findStatusError(ApplicationObject appObject) {
        if (appObject == null ||
            appObject.getStatusListAppDataInq() == null ||
            appObject.getStatusListAppDataInq().size() == 0 ||
            appObject.getStatusListAppDataInq().get(0) == null ||
            appObject.getStatusListAppDataInq().get(0).getSeverity() == null ||
            (appObject.getStatusListAppDataInq().get(0).getSeverity().name()).equals(Severity.ERROR.toString()) ||
            (appObject.getStatusListAppDataInq().get(0).getSeverity().name()).equals(Severity.WARNING.toString()) ||
            (appObject.getStatusListAppDataInq().get(0).getSeverity().name()).equals(Severity.FAULT.toString()))
                return true;
            else
                return false;
        }
    }

Code Formatting Conventions

  • Density
  • Distance
  • Order

Horizontal formatting

  • Density
  • Alignment
  • Indentation

Vertical formatting

Code Formatting Conventions

https://google-styleguide.googlecode.com/svn/trunk/javaguide.html

http://lars-lab.jpl.nasa.gov/JPL_Coding_Standard_Java.pdf

Common rules:

  • Documentation (JavaDoc) conventions
  • Upper-case class names; camel-case variable names
  • New line after conditionals; always using braces
  • One variable per declaration
  • No chained method calls
  • Store common references for reuse
  • Encapsulate and use positive conditionals
  • Declare abstract type, instantantiate implementation

Follow team and company rules

Exception Conventions

  • Fail fast
  • Catch specific exceptions
  • Catch only when it can be handled in a meaningful way
  • Use exceptions rather than return codes
  • Do not use exceptions for control flow
  • Provide context with exceptions
    • Informative error messages
    • Failed operation and values

Lunch

Adventures in Software Craftsmanship

By Reid Harrison

Adventures in Software Craftsmanship

  • 819