Scientific code is easier to adapt and maintain when its structure, expected behavior, and data requirements are clear. This hands-on course develops practical ways to turn research scripts into organized Python projects built from small, reusable functions.

Participants manage environments with uv, track changes with Git, and use continuous integration to check projects across computing environments. They write automated tests with pytest, validate inputs and tabular data, and separate user-facing interfaces from the computations those interfaces invoke.

The final session focuses on measuring performance and identifying bottlenecks before choosing an optimization. Participants explore how computation, data representation, and disk access contribute to runtime, with introductory examples of compiled code and GPU computing.

Course Outline

The course has four weekly sessions. Each session lasts 3.5 hours, for a total of 14 hours of scheduled course time.

Session 1: Project Structure and Reusable Functions

Track code changes with Git and work with GitHub repositories. Manage project environments with uv, organize files into a clear Python project structure, and build modular experiments and analyses with functions. Use continuous integration (CI) runners to automate project checks and assess portability across computing environments.

Session 2: Automated Testing with pytest

Break computations into small parts with clearly defined behavior, then write unit tests with pytest to check their results. Organize and run an automated test suite, and use fixtures to provide reusable test data and setup. Explore how tests help detect unintended changes as research code evolves.

Session 3: Data Validation and Software Boundaries

Make a computation’s data requirements explicit and provide clear errors when inputs do not meet them. Explore validation patterns and typed data validation frameworks, including runtime type checks with beartype and contracts with deal. Validate tabular data with Pandera. Separate interfaces, such as command-line entry points or user interfaces, from internal computations so the same logic can be tested and reused through different entry points.

Session 4: Measuring and Improving Performance

Measure and interpret execution time, use microbenchmarks to examine how code scales, and profile functions and lines to locate bottlenecks. Explore how data representation and disk I/O affect performance beyond in-memory computation. Introduce compiled approaches and GPU computing with CuPy, considering when these approaches are useful and how data movement affects their benefits.

Learning and participation

Topics

  • Project Structure and Reusable Functions: Git and GitHub, environment management with uv, Python project organization, modular computations with functions, and portability checks with CI runners.
  • Automated Testing: Unit tests with pytest, expected behavior, reusable test setup, and pytest fixtures.
  • Validation and Software Boundaries: Data validation patterns, runtime type checks with beartype, contracts with deal, tabular validation with Pandera, and separation of interfaces from internal computation.
  • Performance Measurement and Optimization: Timing, microbenchmarking, scaling behavior, profiling, data representation and disk I/O, and introductions to compiled code and GPU computing with CuPy.

Intended participants

  • Researchers and students from all universities are welcome.
  • Comfort writing small Python scripts and calling functions.
  • Some experience with Git, GitHub, or shared research code.

Certification

Participants who attend at least 75% of the course will receive a participation certificate by email at the end of the course.