Linked handout: 5. Writing code for Humans to read and Machines to run

Writing code for Humans to read and Machines to run

Why worry about "reading"?

    As we already discussed:

  • You will need to read it again (maintainability)
  • Your colleagues might read it (collaboration)
  • You need to understand it (correctness/extensibility)
  • You/your colleagues/others will run it (usability)
  • ... (and more)

Categories of readability/comprehension/usability

Writing code for Humans to read and Machines to run

Categories of readability/comprehension/usability

  • Readable code / style / formatting
  • Software design
  • Comments / docstrings
  • External documentation / manual
  • Examples / tests
  • Changelogs / READMEs / release notes
  • APIs / interfaces
  • CLIs / TUIs / GUIs
  • Configuration
  • Data formats

Lets go trough some things from each

Writing code for Humans to read and Machines to run

  • Readable code / style / formatting

    Some considerations

  • Variable / function / class names
    $\mapsto$descriptive but concise, context matters!
  • Terms / abbreviations / (natural) language
    $\mapsto$clear and defined
  • Limiting line-length
    $\mapsto$consensus = 80-120 ish
  • Using intermediate variables / step-by-step
    $\mapsto$when clarity trumps efficiency

Writing code for Humans to read and Machines to run

  • Readable code / style / formatting
                    
                        def f(x, y): ...

                        # vs

                        def true_anomaly(mean_anomaly, eccentricity): ...
                    
                

Writing code for Humans to read and Machines to run

  • Readable code / style / formatting
                    
                        class AbstractInterruptibleBatchPreparedStatementSetter: ...

                        # vs (with some re-design)

                        class StatementSetter: ...
                    
                

Writing code for Humans to read and Machines to run

  • Readable code / style / formatting
                    
                        def gmf(zx, zr): ...

                        # vs

                        def generalized_match_function(
                            received_signal,
                            transmitted_signal,
                        ): ...
                    
                

Writing code for Humans to read and Machines to run

  • Readable code / style / formatting
                    
                    v[1:-1, 1:-1] = (vn[1:-1, 1:-1] - dt / dx * un[1:-1, 1:-1] * (vn[1:-1, 1:-1] - vn[1:-1, 0:-2]) - dt / dy * vn[1:-1, 1:-1] * (vn[1:-1, 1:-1] - vn[0:-2, 1:-1]) + nu * dt / dx**2 * (vn[1:-1, 2:] - 2 * vn[1:-1, 1:-1] + vn[1:-1, 0:-2]) + nu * dt / dy**2 * (vn[2:, 1:-1] - 2 * vn[1:-1, 1:-1] + vn[0:-2, 1:-1]))

                        # vs
                    v[1:-1, 1:-1] = (
                         vn[1:-1, 1:-1] - 
                         dt / dx * un[1:-1, 1:-1] *
                         (vn[1:-1, 1:-1] - vn[1:-1, 0:-2]) -
                         dt / dy * vn[1:-1, 1:-1] * 
                         (vn[1:-1, 1:-1] - vn[0:-2, 1:-1]) + 
                         nu * dt / dx**2 * 
                         (vn[1:-1, 2:] - 2 * vn[1:-1, 1:-1] + vn[1:-1, 0:-2]) +
                         nu * dt / dy**2 *
                         (vn[2:, 1:-1] - 2 * vn[1:-1, 1:-1] + vn[0:-2, 1:-1])
                    )
                    
                

Writing code for Humans to read and Machines to run

  • Readable code / style / formatting
                    
                    v[1:-1, 1:-1] = (vn[1:-1, 1:-1] - dt / dx * un[1:-1, 1:-1] * (vn[1:-1, 1:-1] - vn[1:-1, 0:-2]) - dt / dy * vn[1:-1, 1:-1] * (vn[1:-1, 1:-1] - vn[0:-2, 1:-1]) + nu * dt / dx**2 * (vn[1:-1, 2:] - 2 * vn[1:-1, 1:-1] + vn[1:-1, 0:-2]) + nu * dt / dy**2 * (vn[2:, 1:-1] - 2 * vn[1:-1, 1:-1] + vn[0:-2, 1:-1]))

                    # vs
                    x_dot = dt / dx * un[1:-1, 1:-1]
                    y_dot = dt / dy * vn[1:-1, 1:-1]
                    v_diff_x = vn[1:-1, 1:-1] - vn[1:-1, 0:-2]
                    v_diff_y = vn[1:-1, 1:-1] - vn[0:-2, 1:-1]
                    v_diff_xy = vn[1:-1, 2:] - 2 * vn[1:-1, 1:-1] + vn[1:-1, 0:-2]
                    v_diff_yx = vn[2:, 1:-1] - 2 * vn[1:-1, 1:-1] + vn[0:-2, 1:-1]

                    v[1:-1, 1:-1] += (
                         - x_dot * v_diff_x - y_dot * v_diff_y
                         + nu * dt / dx**2 * v_diff_xy
                         + nu * dt / dy**2 * v_diff_yx
                    )
                    
                

Writing code for Humans to read and Machines to run

  • Readable code / style / formatting
                    
                        def foo(x): ...
                            """Den här dokumentationen kan bara läsas av de som kan svenska!
                            """

                        # vs

                        def foo(x): ...
                            """This documentation can only be read by people who know swedish!
                            """
                    
                

Writing code for Humans to read and Machines to run

  • Readable code / style / formatting

Lets test some practical considerations

  • Install flake8 or integrate it into your editor
  • Run it on some of your code
  • Disable some of the warning that are strange, edit line length, (use the .flake8 file)
  • Auto format the code with black or ruff

Writing code for Humans to read and Machines to run

  • Readable code / style / formatting
  • Software design
  • Comments / docstrings
  • External documentation / manual
  • Examples / tests
  • Changelogs / READMEs / release notes
  • APIs / interfaces
  • CLIs / TUIs / GUIs
  • Configuration
  • Data formats

Writing code for Humans to read and Machines to run

  • Software design

This is a tricky one... but there is an attempt

(Plot idea stolen from "Software Design by Example" by Greg Wilson)

Writing code for Humans to read and Machines to run

  • Software design

Lets unpack the above a bit

  • Your comprehension of the code/project/module/...
  • Exports of course comprehend more
  • Peak is further into abstraction
  • But it still dips at both ends!
  • You maybe noticed during practical:
    interfaces and collaboration!

Writing code for Humans to read and Machines to run

  • Software design

Well defined interfaces are easy to grasp and use
The interface to the entire frames sub-package

                    
                        def convert(t, states, in_frame, out_frame, **transform_kwargs):
                            ...
                    
                
                    
                        from sorts import frames
                        ...
                        states = frames.convert(t, states, "TEME", "ITRS")
                    
                

Writing code for Humans to read and Machines to run

  • Software design

    Some more considerations

  • Function size
    not too short (jumping everywhere)
    not too long (hard to grasp)
  • Side-effects
    limit them! (hard to follow)
  • Avoid cyclic complexity!
    clear separation is always easier to grasp and debug

Writing code for Humans to read and Machines to run

  • Software design

Let us re-visit during the design lecture!

Writing code for Humans to read and Machines to run

  • Readable code / style / formatting
  • Software design
  • Comments / docstrings
  • External documentation / manual
  • Examples / tests
  • Changelogs / READMEs / release notes
  • APIs / interfaces
  • CLIs / TUIs / GUIs
  • Configuration
  • Data formats

Writing code for Humans to read and Machines to run

  • Comments / docstrings

    Some recommendations

  • Don't write useless comments
    Write readable code instead
  •                         
                                # Add a and b
                                c = a + b
                            
                        
  • Write a docstring when needed
  •                         
                                def complex_doppler_drift_phasor(...):
                                    """Calculates the phase offsets (as complex numbers)
                                    that a signal gets shifted by when the Doppler shift is
                                    drifting linearly.
    
                                    """
                            
                        
  • Don't forget module level docstrings!
  • Use a standard docstring format

Writing code for Humans to read and Machines to run

  • Comments / docstrings
  • Use a standard docstring format
  • type hinting vs docstrings - why not both or neither
  • My recommendation: numpydoc
  • Lets look at an example

Soooo "rendering"?

Writing code for Humans to read and Machines to run

  • Readable code / style / formatting
  • Software design
  • Comments / docstrings
  • External documentation / manual
  • Examples / tests
  • Changelogs / READMEs / release notes
  • APIs / interfaces
  • CLIs / TUIs / GUIs
  • Configuration
  • Data formats

Writing code for Humans to read and Machines to run

  • External documentation / manual

Use a static website / document builder with a markup language

    markup languages to use

  • (recommendation) Markdown
  • reStructuredText
  • HTML
  • typst link

    Documentation building

  • (recommendation) mkdocs
  • sphinx
  • pandoc
  • hugo
  • ...

Writing code for Humans to read and Machines to run

  • External documentation / manual

Use a static website / document builder with a markup language

Examples!

Writing code for Humans to read and Machines to run

  • Readable code / style / formatting
  • Software design
  • Comments / docstrings
  • External documentation / manual
  • Examples / tests
  • Changelogs / READMEs / release notes
  • APIs / interfaces
  • CLIs / TUIs / GUIs
  • Configuration
  • Data formats

Writing code for Humans to read and Machines to run

  • Examples / tests

    Accelerates understanding!

  • Make an examples/ folder
  • Find a balance - too complicated examples hard to grasp
    trivial examples don't add anything
  • Write an example in the docstring
  • Tests also show usage!

Writing code for Humans to read and Machines to run

  • Readable code / style / formatting
  • Software design
  • Comments / docstrings
  • External documentation / manual
  • Examples / tests
  • Changelogs / READMEs / release notes
  • APIs / interfaces
  • CLIs / TUIs / GUIs
  • Configuration
  • Data formats

Writing code for Humans to read and Machines to run

  • Changelogs / READMEs / release notes
  • Chagenlogs: helps users keep up to date
  • READMEs: provides the entry point!
  • Release notes: helps users make decisions
  • Git tags: helps users find their way

Writing code for Humans to read and Machines to run

  • Readable code / style / formatting
  • Software design
  • Comments / docstrings
  • External documentation / manual
  • Examples / tests
  • Changelogs / READMEs / release notes
  • APIs / interfaces
  • CLIs / TUIs / GUIs
  • Configuration
  • Data formats

Writing code for Humans to read and Machines to run

  • APIs / interfaces
  • Describe your APIs!
  • Describe your interfaces!

Writing code for Humans to read and Machines to run

  • Readable code / style / formatting
  • Software design
  • Comments / docstrings
  • External documentation / manual
  • Examples / tests
  • Changelogs / READMEs / release notes
  • APIs / interfaces
  • CLIs / TUIs / GUIs
  • Configuration
  • Data formats

Writing code for Humans to read and Machines to run

  • CLIs / TUIs / GUIs
  • CLI: use argparse or standard package
    write the help strings!
  • TUIs: remember to add a in-TUI help!
    (e.g. urwid)
  • GUIs: never force "clicking"
    where programmatic interface should be available,
    add a "manual" or video tutorial or similar

Writing code for Humans to read and Machines to run

  • Readable code / style / formatting
  • Software design
  • Comments / docstrings
  • External documentation / manual
  • Examples / tests
  • Changelogs / READMEs / release notes
  • APIs / interfaces
  • CLIs / TUIs / GUIs
  • Configuration
  • Data formats

Writing code for Humans to read and Machines to run

  • Configuration
  • Data formats
  • Use human readable configs
  • Use standard formats
  • Think why plain-text / binary

Writing code for Humans to read and Machines to run

Last but not least:

who is your reader?

Writing code for Humans to read and Machines to run

Further study and homework

Let's check out the handout above!