A Code Style Guide

Computer code is read by two different audiences: computers, which execute the code statements (or render the web page), and humans who need to interpret and update the code over time. Using good coding style—the manner and format in which code is written—is vital for supporting the humans who read it. Poorly written code may be interpretable by the computer and thus work correctly, but it also needs to be understandale by people.

Code that is well written (has “good style”) fulfills two properties:

  • It is easy for people to read
  • It is easy for people to modify in the future

All coding style guidelines should follow from these two goals. If you ever wonder whether one styling approach is better than another, consider it in terms of those goals.

Everyone has a differnet opinions on what is considered “good style”; indeed there are numerous existing “style guides” written for web programming languages: for example, Google has their own HTML/CSS and JavaScript style guides, as does Mozilla. There are also tools that enforce styling guidelines. “linters” such as ESLint will both identify syntactic errors, but also may have strong opinions on style, marking “poor style” as an error.

I recommend being cautious with automatic code styling (“beautifier”) tools or plugins. While these can be useful, it’s also possible that they will “clean” your code in a way that doesn’t actually improve it. Be very careful with anything that writes or changes code for you to ensure that it’s doing the right thing!

This chapter provides a number of specific guidelines following the opinions of the authors (and instructors of the INFO 340: Client Side Development course) in order to help you learn to write code that is generally easier to read and modify—that has good style. It is not intended to be comprehensive; rather it is a collection of guidelines that students often question or have troubles following intuitively. See the above linked style guides for more comprehensive suggestions.

This chapter is a work in progress, with more guidelines being added as they come up.

A.1 HTML Guidelines

  • Always use lowercase letters for HTML tags. This helps with readability and consistency.

    <!-- Do this -->
    <p>lorem ipsum</p>
    
    <!-- Don't do this -->
    <P>lorem ipsum</P>
    <P>lorem ipsum</p>

Spacing

  • In general, put tags for block elements on their own lines, with the content of block elements as a separate (indented) line—unless the content of that block element is very short. Subsequent block eleents are indented an additional step. This makes it easier to read the code by seeing the blocks, as well as to modify the code by adding more content inside of blocks.

    <!-- Do this -->
    <div>
      lorem ipsum
    </div>
    
    <!-- Do this -->
    <div>
      <p>
       lorem ipsum
      </p>
    </div>
    
    <!-- Do this -->
    <p>Hello world</p> <!-- short content so can be on same line as tags -->
    
    <!-- Don't do this -->
    <div><p>lorem ipsum</p></div>
  • For many inline elements—particularly text formatting ones (e.g., <em>, <strong>, <a>)—it’s best to keep them inline with the rest of the content. Think about like writing a paragraph, but some words in the middle are formatted. Don’t try to separate out the inline elements. This makes it easier to see them as “inline”, and means that you can integrate it into your code without needing to worry about spacing. (This will also often fix trailing space issues). Note that this guideline is one that automated beautifying tools mess up.

    <!-- Do this -->
    <p>
      There was a farmer who had a dog, and <em>Bingo</em> was his name-o...
    </p>
    
    <!-- Don't do this -->
    <p>
      There was a farmer who had a dog, and
      <em>Bingo</em>
      was his name-o...
    </p>

    For “structural” inline elements (e.g., <img> , <button>), it’s better to put them on their own line similar to block elements. Indeed, these elements may often want to be styled as block elements anyway!

    <!-- Do this -->
    <p>
      This is a picture of a dog:
      <img src="puppy.png" alt="a puppy">
    </p>
    
    <!-- Don't do this -->
    <p>
      This is a picture of a dog: <img src="puppy.png" alt="a puppy">
    </p>
  • Element attributes should be written on the same line as the element tag they apply to—even if the attribute value seems “long” (like a URL).

    <!-- Do this -->
    <a href="https://info340.github.io/really/long/path/to/content">Link</a>
    
    <!-- Don't do this -->
    <a
      href="https://info340.github.io/really/long/path/to/content">Link</a>

    Note that it is not valid HTML to put whitespace, including line breaks, around the = when defining an attribute.

    The one exception to this style guideline is when you have lots of attributes for a single element. In that situation, it’s acceptible to put each attribute on its own line, indented 1 step. While that rarely happens in straight HTML, it can be very common when specifying props for a React component (and makes it easier to modify those props as well).

    <!-- Do this -->
    <input
      type="text"
      id="user-input-field"
      name="user-input-field"
      value=""
      placeholder="Write something here!"
      class="long-form-input">
  • Write all text content of a single element as a single line of code; do not manually put line breaks inside of plain text. This will allow you to modify that text content later without needing to reformat things. Do not worry about the length of the line of code. You can use the “word wrap” functionality of your editor to avoid horizontal scrolling.

    <!-- Do this -->
    <!-- This *single line of code* can be made to wrap in your editor -->
    <p>
      Lorem, ipsum dolor sit amet consectetur adipisicing elit. Consectetur est necessitatibus, rerum atque officiis doloremque porro similique molestias fugit, a repellendus fuga natus, tempora impedit. Dolore repellendus itaque soluta est ad modi corrupti quibusdam tenetur architecto nesciunt harum ipsa consectetur ullam unde, quos sit asperiores corporis vitae pariatur expedita non?
    </p>
    
    <!-- Don't do this -->
    <p>
      Lorem, ipsum dolor sit amet consectetur adipisicing elit.
      Consectetur est necessitatibus, rerum atque officiis doloremque
      porro similique molestias fugit, a repellendus fuga natus,
      tempora impedit. Dolore repellendus itaque soluta est ad modi
      corrupti quibusdam tenetur architecto nesciunt harum ipsa
      consectetur ullam unde, quos sit asperiores corporis vitae
      pariatur expedita non?
    </p>

Specific Elements

  • Avoid non-semantic formatting elements (e.g., <i>, <b>), as they are not accessible to screen readers.

  • Avoid using the <br> element for line breaks. If you’re breaking a line of text, it’s most often because you’re defining a new paragraph, and so should use an additional <p> element. If there isn’t a semantic meaning for the line break, don’t include one! If you need to adjust the amount of spacing between paragraphs (e.g., you want it to be a single spacing not double spacing), use CSS to adjust the margin or padding.

    <!-- Do this -->
    <p>
      Lorem, ipsum dolor sit amet consectetur adipisicing elit.
    </p>
    <p>
      Consectetur est necessitatibus, rerum atque officiis doloremque porro similique molestias fugit, a repellendus fuga natus, tempora impedit.
    </p>
    
    <!-- Don't do this -->
    <p>
      Lorem, ipsum dolor sit amet consectetur adipisicing elit.
      <br />
      Consectetur est necessitatibus, rerum atque officiis doloremque porro similique molestias fugit, a repellendus fuga natus, tempora impedit.
    <p>

    The only reason I can think of for semanticaly using a <br> element would be in poetry:

    <!-- Do this -->
    <div>
      Roses are red, <br>
      Violets are blue. <br>
      Unexpected '}' <br>
      on line 32.
    </div>

Comments in HTML

  • Most HTML content doesn’t need comments; it should be understandable just from the indentation and content. If your code needs more organization, use semantic elements (e.g., <section>) or class names to help structure it (e.g., <div class="first-post">).

A.2 CSS Guidelines

One piece of adviace for writing good CSS is to focus on the “minimal” amount of CSS needed to achieve your effect. Work with the browser and its defaults, rather than trying to overpower it. Perhaps surprisingly, removing rules or properties is usually a better fix to a problem then adding additional styling.

A.2.1 Spacing

  • When writing CSS rules, put the { of a rule on the same line as the selector, and indent the properties of the rule a consistent amount. Put a space after the : in a rule, but not before. Rules should thus look like:

    selector {
        property: value;
        property: value;
    }

    Note that you can use VS Code to automatically “indent” your code using the format document command.

A.2.2 Selectors

  • Use selectors that are only as specific as they need to be.

    /* Do this */
    li.selected {} /* selected list items, not selected paragraphs */
    
    /* Don't do this */
    div p {} /* paragraphs inside of divs (are there other paragraphs? */
    
    /* Don't do this */
    body p {} /* no paragraphs outside of the body, so redundant */

    (It’s hard to show examples, because the required specificity depends on the situation!)

    Most selectors will involve 1-3 “pieces” (connected by descendant or compound selection). If you find yourself using more than that, ask if there’s a simpler way to write the selector—or just give the element a class to select it directly!

  • Use class or element selectors instead of id selectors.

    /* Do this */
    .side-nav {}
    
    /* Don't do this */
    #side-nav {}
  • If you must use an id selector, don’t add extra specificity before id selectors. An id has to be unique, so you don’t need to distinguish between multiple elements with the same id.

    /* Don't do this */
    nav#side-nav {}
  • Do not use the !important keyword. Write a sufficiently specific rule instead!

    /* Don't do this */
    .error {
      color: red !important;
    }
    
    /* Do this */
    p.alert.error {
      color: red;
    }

A.2.3 Class Names

  • Use descriptive class names. Classes should describe what or why that styling is being applied.

    /* Do this */
    .side-nav {}
    
    /* Do this */
    .alert-warning {}
    
    /* Don't do this */
    .thingy {}
    
    /* Don't do this */
    .s1 {}
    
    /* Don't do this */
    .p {} /* use an element selector instead! */

    Note that classes could either be defined semantically (e.g., .avatar-icon, .comment-form), or modularly (e.g., .font-large, .bg-secondary). Either approach is acceptable per this guide, but try to be consistent.

    Using naming schema such as BEM is also acceptable.

  • Use hyphens as delimiters for class names.

    /* Do this */
    .side-nav {}
    
    /* Do this */
    .alert-warning {}

    Certain frameworks may make it sensible to use camelCasing for class naming (so class names are also valid JavaScript identifiers). In that case, be consistent with delimiters. This will make it easier to write and modify the code.

    /* Don't do this */
    .sideNav {}
    .alert-warning {}

A.2.4 Specific Properties

  • Avoid using float; use flexboxes or grids for positioning. It is rare to actually have “floating” content (though it can happen with image inserts inside of large text-based articles).

  • Avoid using position: absolute. This will produce layouts that are not responsive or accessible to multiple devices. Work with the browser’s layout instead by using relative layouts.

A.2.5 Responsive CSS

  • Use a mobile-first approach to styling. This means that general rules go at the top and apply to mobile devices, and then use media queries to specify style alterations for larger displays.

  • Media queries check against minimize size only:

    /* Do this */
    @media (min-width: 768px) {}
    
    /* Do not do this */
    @media (min-width: 768px) and (max-width: 1092px) {}
    
    /* Do not do this */
    @media (max-width: 768px) {}
  • Limiting styling rules to screen is not required (unless you distinctly want those to be different from printed rules)

    /* Do this */
    @media (min-width: 768px) {}
    
    /* Do not need to do this */
    @media screen and (min-width: 768px) {}
  • Media queries should be used to modify the mobile device styling, not to replace it completely. Don’t “reset” all of the rules from the mobile styling, just override and add-to the few rules needed to make the page effectively responsive. Your page should have a similar style/theme no matter what device is being used to view the content!

A.2.6 Comments in CSS

  • You do not need a lot of comments for CSS code; your rules should be self-explanatory because you’ve used descriptive class names.

  • Use comments to help organize or “sign-post” your code, to group rules together in your .css file.

    /* Do this */
    
    /* navbar */
    nav {}
    nav li {}
    .tab-selected {}
    
    /* main content */
    section {}
    p {}
    img.small {}

A.3 JavaScript Guidelines

A.3.1 Variables

  • Declare variables using const or let. In general, use const for all variables, and then let only if you find you need to reassign the variable later (which is a lot less common then you may think)!

    Do not use var to declare variables (this avoid polluting the global scope).

  • Variable names are written in camelCaseFormat (capitalizing the first letter of each “word” in the name after the first). Do not begin variable names with a capital letter.

    /* Do this */
    const myName = "Joel Ross"
    
    /* Do not do this */
    const my_name = "Joel Ross"
    
    /* Do not do this */
    const MyName = "Joel Ross"

    You can use all capital letters for global constants. Note that just because a variable is declared const doesn’t mean it is a “global constant”. Use all capital letters to name values that are specified external to the functioning of your program; PI, WA_TAX_RATE, SCREEN_WIDTH are all global constants.

  • Use descriptive variable names. Variable names should what the value references. Do not use names like stuff, thing, or x as they won’t help anyone understand your code.

  • Name arrays and collections using plural nouns. Alternatively, you may find it useful to name strings, arrays, objects, and functions after their data type to help you remember (particularly if understanding plurals in English is difficult).

    /* Do this */
    const names = ["john", "paul", "george", "ringo"];
    
    /* Do this */
    const dogArray = ["fido", "spot", "rover"]; //array of dog names
    const dogObj  = {name: 'Fido', breed: 'mutt'}; //single dog object
    
    /* Do not do this */
    const dog = ["fido", "spot", "rover"]; //an array named by a singular noun

A.3.2 Functions

  • Use function declarations rather than function expressions when defining functions—particularly “top-level” functions. This helps distinguish between functions and variables, making it easier to read and follow the code (even if it takes more typing).

    /* Do this */
    function sum(a, b) {
      return a + b;
    }
    
    /* Do not do this */
    const sum = function(a, b) {
      return a + b;
    }
    
    /* Do not do this */
    const sum = (a, b) => a + b;
  • For inline functions (such as anonymous callback functions), use arrow notation. This can keep things more concise and also avoid some scoping problems.

    /* Do this */
    const transformed = array.map((item) => {
      //...
    })
    
    /* Do not do this */
    const transformed = array.map(function(item) {
      //...
    })

    When using arrow functions, always put the () around the argument list. This clarifies that it is a function and will make things easier if/when you want to add additional arguments.

    /* Do this */
    const transformed = array.map((item) => {
      //...
    })
    
    /* Do not do this */
    const transformed = array.map(item => {
      //...
    })

    In general, avoid using concise body arrow functions. Including the explicit block (the {}) makes it easier to read as a function, as well as to modify and debug since you can add additional statements easily. It may seem like more code to type, but it’s better style.

    /* Do this */
    const exclaimed = stringArray.map((aString) => {
      return aString + "!!" //add exclamation points
    })
    
    /* Do not do this */
    const exclaimed = stringArray.map((aString) => aString + "!!")
  • Functions should be defined to be short and reusable. Using pure functions whenever possible. This means that they avoid side effects—they do not assign to or modify non-local variables.

A.3.3 Comments in JavaScript

“Comments are always failures.” - Robert Martin

In general well-written code documents itself without the need for additional comments. Functions that are well named with small scopes will clearly indicate what they do without the need for further notation. If you find that you need a comment to explain what your code does, then you probably should rewrite that code so that it’s more readable. Comments should be used to provide further information about the intent of code (why it has been included), not the behavior of code (what it does).

This means that in general you shouldn’t need to include a lot of comments in your code. Use comments as a last resort to clarify code behavior or otherwise communicate with other people.

Do not retain large blocks of commented code in production (final) versions—be clear about the code you’re using without requiring the user to scroll past or ignore a lot of code that you’re not.

A.3.4 Miscellaneous JavaScript Guidelines

  • Use strict equality comparisons (=== and !==) instead of regular equality comparisons (== and !=).

  • Minimize the use of console.log() statements in production (final) versions. These will slow your program down, pollute the logging space, and can be a source of information and security leaks. Use lots of console.log() statements when debugging, but remove them when you’ve fixed the bugs.

A.4 React Guidelines

A.4.1 Components

  • Use Component functions (and hooks). Do not define components as classes.

  • Use function declarations rather than function expressions when defining Component functions. This helps with readability.

    /* Do this */
    function Card(props) {
      //...
    }
    
    /* Do not do this */
    const Card = (props) => {
      //...
    }
  • Component functions are named using nouns (what they are), not verbs (what they do). You name then like you would name classes in Java or other OOP languages.

    /* Do this - what the content is */
    function EntryForm(props)
      //...
    }
    
    /* Do not do this - what it does */
    function UpdateData(props) {
      //...
    }

    Give Components descripive names that indicate what “kind” of element they are, so there is no confusion about whether they are nouns or verbs.

    /* Do this - the component is a form */
    function FilterForm(props)
      //...
    }
    
    /* Do not do this - unclear what the component is */
    function Filter(props) {
      //...
    }
  • Components are always written as top level functions. Never define one component inside of another!

    /* Do this */
    function Parent(props)
      //...
    }
    
    function Child(props)
      //...
    }
    
    
    /* Do not do this */
    function Parent(props) {
    
      function Child(props) {
        //...
      }
    
      //...
    }
  • The argument to a Component is always called props (with an s at the end!).

    It is acceptable to use object destructuring in the argument to a Component function. Remember to include the {}—a Component only accepts a single argument!

    /* Acceptable */
    function SongCard(props)
      //...
    }
    
    /* Also acceptable */
    function SongCard({artist, title, album}) {
      //...
    }
  • When specifying props to a Component (or attributes for an HTML element), do not put spaces around the = — write it like HTML!

    /* Do this */
    <Card value={dataItem} />
    
    /* Do not do this */
    <Card value = {dataItem} />
  • Organize Components into separate modules (.js files). You can include multiple related components (e.g,. a Card and a CardList) in the same module. It’s a good idea to keep these files together inside of a distinct components folder in your source code.

A.4.2 Mapping Data

  • Do not declare functions such as the .map() callback inline inside the return statement of a Component (or any other function). Use multiple statements when writing code!

    Instead, declare the mapped values as a separate variable that can be included inside of the returned DOM. This will make your code easier to read, modify, and debug since you can add additional processing or inspections after the .map() call.

    /* Do this */
    function App(props) {
      //map the data into <Card> elements
      const cardElemArray = data.map((dataItem) => {
        return <Card value={dataItem} key={dataItem.id} />
      })
    
      return (
        <div>
          {cardElemArray}
        </div>
      )
    }
    
    /* Do not do this */
    function App(props) {
      //This is all one statement (line) of code! Don't do that.
      return (
        <div>
          {
            data.map((dataItem) => {
              return <Card value={dataItem} key={dataItem.id} />
            })
          }
        </div>
      )
    }

A.4.3 State

  • Always use const when declaring state variables (since you don’t reassign them anyway)!

    /* Do this */
    const [data, setData] = useState([]);
    
    /* Do not do this */
    let [data, setData] = useState([]);
  • Always name the “state setter” function after the name of the state variable (set______):

    /* Do this */
    const [data, setData] = useState([]);
    const [currentsong, setCurrentSong] = useState({});
    
    /* Do not do this */
    const [nowPlaying, changeSong] = useState({});
    const [lastTrack, setLast] = useState({}); //use the full variable name instead!
  • Do not duplicate data in state. It’s both bad style and will cause bugs. Keep state minimal.

    Do not define state variables for values that can be computed from other variables.

    /* Do this */
    const [myArray, setMyArray] = useState([]);
    const arrayLength = myArray.length; //computed from state, but not a state variable
    
    /* Do not do this */
    const [myArray, setMyArray] = useState([]);
    const [arrayLength, setArrayLength] = useState(0); //duplicated data

    Do not have multiple components save the same data in their state. Instead, lift the state up to the appropriate level and pass the data down as a prop.

    /* Do this */
    function App(props) {
      const [data, setData] = useState([]);
    
      return (
        {/* pass down the data as a prop */}
        <CardList data={data} />
      )
    }
    
    
    /* Do not do this */
    function App(props) {
      const [data, setData] = useState([]);
    
      return (
        <CardList data={data} />
      )
    }
    
    function CardList(props) {
      const [data, setData] = useState(props.data);
      //...
    }
  • Do not pass state setter functions as callbacks to child components. Instead, create a separate callback function which can call the state setter. This makes it easier to add additional processing and control logic when updating state, as well as following the Principle of Least Knowledge

    /* Do this */
    function App(props) {
      const [data, setData] = useState([]);
    
      //a function to update the data
      const updateData = (newData) => {
        //can do additional processing here
        setData(newData); //update the state
      }
    
      return (
        {/* pass down the callback function as a prop */}
        <UpdateForm data={data} updateCallback={updateData} />
      )
    }
    
    
    /* Do not do this */
    function App(props) {
      const [data, setData] = useState([]);
    
      return (
        {/* Do not pass the state setter directly! */}
        <UpdateForm data={data} updateCallback={setData} />
      )
    }

A.4.4 Events and Forms

  • All forms should be controlled, managing the inputted value through the Component’s state (instead of the HTML element’s state). This helps avoid data duplication, following the Don’t Repeat Yourself (DRY) Principle.

    /* Do this */
    function ExampleForm(props) {
      const [inputValue, setInputValue] = useState('')
    
      const handleChange = (event) => {
        let newValue = event.target.value
        setInputValue(newValue);
      }
    
      return (
        <input type="text" onChange={handleChange} value={inputValue} />
      )
    }

A.5 Miscellaneous Guidelines

  • Name all files and folder (source code or otherwise) with all lowercase letters. This will keep things consistent and avoid bugs across operating systems.

  • Do not include spaces () in file names, particular for media assets—do not have a file named my puppy.png. Instead replace the space with an alternate character, such as an _, a -, or a +. Whitespace characters need to be specially encoded for URIs, which can cause problems and readability issues.