diff --git a/_quarto.yml b/_quarto.yml
index a196151a9b7a993f8fee7f03566c53ca8acbbea8..fd031fa15534a068f976979ff2dc2d19f6b63ddc 100644
--- a/_quarto.yml
+++ b/_quarto.yml
@@ -33,7 +33,7 @@ website:
           - "lectures/complexity/slides.qmd"
           - "lectures/debugging-strategies/slides.qmd"
           # - "lectures/good-scientific-practice/slides.qmd"
-          # - "lectures/user-experience/slides.qmd"
+          - "lectures/user-experience/slides.qmd"
           # - "lectures/testing/slides.qmd"
           # - "lectures/git2/slides.qmd"
           # - "lectures/parallelism/slides.qmd"
@@ -49,7 +49,7 @@ website:
           - "exercises/complexity.qmd"
           - "exercises/debugging-strategies.qmd"
           # - "exercises/good_scientific_practice.qmd"
-          # - "exercises/user_experience.qmd"
+          - "exercises/user-experience.qmd"
           # - "exercises/testing.qmd"
           # - "exercises/git2.qmd"
           # - "exercises/parallelism.qmd"
diff --git a/exercises/user-experience.qmd b/exercises/user-experience.qmd
new file mode 100644
index 0000000000000000000000000000000000000000..bb0357bea4c018e737f7eda3c25e13ce04549fb8
--- /dev/null
+++ b/exercises/user-experience.qmd
@@ -0,0 +1,15 @@
+---
+title: "User experience design"
+---
+
+**1. Compare the user experience for accessing dropsonde datasets from two different campaigns and name 5 good/bad points that relate to one or the other dataset.**
+
+* Dropsondes from the EUREC4A measurement campaign: [https://howto.eurec4a.eu/dropsondes.html](https://howto.eurec4a.eu/dropsondes.html)
+* Dropsondes from the NARVAL2 measurement campaign: [https://www.wdc-climate.de/ui/entry?acronym=HALO_measurements_3](https://www.wdc-climate.de/ui/entry?acronym=HALO_measurements_3)
+
+Get the data, open the datasets and make a simple plot of e.g. the humidity profile of the first dropsonde of the campaign.
+What are the main or most striking differences when working with the datasets?
+
+**2. Take two of your own scripts, check them in, optimize their behavior, and make additional commits fixing the behavior. Explain how you improved them in the commit messages.**
+
+If you don't have any scripts of your own, take [The dropsonde comparison](https://easy.gems.dkrz.de/Processing/healpix/joanne_comparison.html) and another script of your choice from [https://easy.gems.dkrz.de/Processing/healpix/index.html](https://easy.gems.dkrz.de/Processing/healpix/index.html) and turn them into scripts you can call from the command line with sensible arguments and exceptions in case of bad arguments.
diff --git a/lectures/user-experience/slides.qmd b/lectures/user-experience/slides.qmd
new file mode 100644
index 0000000000000000000000000000000000000000..6cb7c735ebdeb56acc584bc27411a9b8ca2efa56
--- /dev/null
+++ b/lectures/user-experience/slides.qmd
@@ -0,0 +1,512 @@
+---
+title: "User Experience Design"
+author: "Theresa Mieslinger and Florian Ziemen"
+---
+
+# Elements of UX design
+
+## Definition
+> **User experience (UX)** is how a user interacts with and experiences a product, system or service. It includes a person's perceptions of **utility**, **ease of use**, and **efficiency**. -Wikipedia
+
+## Where do you encouter UX design in your everyday life? {.special}
+
+::: {.notes}
+* app handling / interaction with touch screens
+* examples: car, step length in stairs, take a foto of the MPIM door opener board :)
+* standards, consistency, logical flow
+* if intuition matches reality
+* software example: a config parameter "enable_convection" in ICON should turn on convection everywhere (!) not only in atm
+:::
+
+## Overview
+* user research
+* visual design
+* interaction design / usability
+* information architecture
+
+# User Research
+
+## Definition
+> User research focuses on understanding user behaviors, needs and motivations through interviews, surveys, usability evaluations and other forms of feedback methodologies. - Wikipedia
+
+## Who are typical users?
+::: {.fragment}
+* you!
+* your peers
+* reviewers of a paper
+* other scientists
+* (including yourself in the future)
+:::
+
+
+## Utility
+a user actually wants to have a product
+
+## User research tools
+* talk to users
+* make a survey
+* create Personas
+
+# Visual design
+
+## Colors {.leftalign}
+* use common color codes (e.g. <span style="color:green;">green = OK</span>, <span style="color:red;">red = Problem</span>)
+* be inclusive and dont't fully rely on colors
+
+![Typical Labview inteface [NCAR docu for the AVAPS software](https://halodrops.readthedocs.io/en/latest/handbook/operations/pre_flight.html#test-chassis-cards)](static/gui_example.png){width="50%"}
+
+::: {.smaller}
+see also [Apple Design Guidelines](https://developer.apple.com/design/human-interface-guidelines/color)
+:::
+
+::: {.notes}
+* maybe you all got it right away that the blue font markes a link to the doku ;-)
+:::
+
+## Typography {.leftalign}
+> Typography is the art and technique of arranging type to make written language legible, readable and appealing when displayed. [- Wikipedia](https://en.wikipedia.org/wiki/Typography)
+
+* Use readable fonts and fontsize
+
+## Layout {auto-animate=true}
+```{python}
+import matplotlib.pylab as plt
+import numpy as np
+
+x = np.linspace(1, 25, 100)
+plt.figure(dpi=200)
+plt.plot(x, np.ones_like(x), label=r"$\mathcal{O}(1)$", ls=(0, (5, 10)), color="C4")
+plt.plot(x, 1+np.log(x), label=r"$\mathcal{O}(\log{n})$", ls="-.", color="C3")
+plt.plot(x, x, label=r"$\mathcal{O}(n)$", ls=":", color="C2")
+plt.plot(x, 1 + x * np.log(x), label=r"$\mathcal{O}(n \log{n})$", color="C1")
+plt.plot(x, x**2, label=r"$\mathcal{O}(n^2)$", ls="--", color="C0")
+plt.xlabel("Number of elements n", fontsize=14)
+plt.ylabel("Time or Space", fontsize=14)
+plt.xlim(1, 25)
+plt.ylim(0, 25)
+plt.legend(fontsize=16)
+plt.grid(True)
+None
+
+```
+::: {.notes}
+* data to ink ratio
+* intuitive line color and their arrangement
+:::
+
+## Layout {auto-animate=true}
+```{python}
+import matplotlib.pylab as plt
+import numpy as np
+
+x = np.linspace(1, 25, 100)
+plt.figure(dpi=200)
+plt.plot(x, x**2, label=r"$\mathcal{O}(n^2)$", ls="--", color="C0")
+plt.plot(x, 1 + x * np.log(x), label=r"$\mathcal{O}(n \log{n})$", color="C1")
+plt.plot(x, x, label=r"$\mathcal{O}(n)$", ls=":", color="C2")
+plt.plot(x, 1+np.log(x), label=r"$\mathcal{O}(\log{n})$", ls="-.", color="C3")
+plt.plot(x, np.ones_like(x), label=r"$\mathcal{O}(1)$", ls=(0, (5, 10)), color="C4")
+plt.xlabel("Number of elements n", fontsize=14)
+plt.ylabel("Time or Space", fontsize=14)
+plt.xlim(1, 25)
+plt.ylim(0, 25)
+plt.xticks([], [])
+plt.yticks([], [])
+plt.legend(fontsize=16)
+plt.gca().spines[['right', 'top']].set_visible(False)
+None
+
+```
+## Layout
+![](static/home_page_example.png)
+
+::: {.notes}
+* you expect a navigation bar at the top or left
+* main content is catchy and easy to grasp and provides a link to continue. It leads to the page a user should read next or might be most interested to check out next.
+* upper right corner: links to source code / community / outreach platforms
+:::
+
+## Templates
+* if you don't want to spend time, use a template / theme
+* e.g. Sphinx, jupyter-book, quarto, HUGO, matplotlib themes
+
+# Interaction Design / Usability
+
+## Interaction design
+
+***imagine** how good interaction could be*
+
+* **goal-oriented design** - satisfying the needs and desires of the user
+* **cognitive dimensions** - a framework to evaluate design solutions
+
+:::{.notes}
+* opposed to goal-oriented design: interfaces can be designed to serve the needs of the service/product provider. User needs may be poorly served by this approach. 
+* how intuitive is a design solution? A/B testing
+* what's the mental load?
+:::
+
+## Example: Git
+Git has a super nice interaction design!
+
+``` {bash}
+git --help
+usage: git [-v | --version] [-h | --help] [-C <path>] [-c <name>=<value>]
+           [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
+           [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--bare]
+           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
+           [--config-env=<name>=<envvar>] <command> [<args>]
+
+These are common Git commands used in various situations:
+
+start a working area (see also: git help tutorial)
+   clone     Clone a repository into a new directory
+   init      Create an empty Git repository or reinitialize an existing one
+...
+
+'git help -a' and 'git help -g' list available subcommands and some
+concept guides. See 'git help <command>' or 'git help <concept>'
+to read about a specific subcommand or concept.
+See 'git help git' for an overview of the system.
+```
+
+## Example: function {auto-animate=true}
+```{python}
+#| echo: true
+def is_atlantic(lon=0, lat=0):
+    return ((lon > 295) | (lon < 20)) & (lat > -30) & (lat < 30)
+```
+
+## Example: function {auto-animate=true}
+```{python}
+#| echo: true
+def is_atlantic(lon, lat):
+    return ((lon > 295) | (lon < 20)) & (lat > -30) & (lat < 30)
+```
+*use **sensible** defaults*
+
+## Example: function {auto-animate=true}
+```{python}
+#| echo: true
+def is_tropical_atlantic(lon, lat):
+    return ((lon > 295) | (lon < 20)) & (lat > -30) & (lat < 30)
+```
+*don't lie :)*
+
+## Example: function {auto-animate=true}
+```{python}
+#| echo: true
+def is_tropical_atlantic(lon, lat):
+    lon = lon % 360
+    return ((lon > 295) | (lon < 20)) & (lat > -30) & (lat < 30)
+```
+*make it easy to do things right*
+
+## Example: function {auto-animate=true}
+```{python}
+#| echo: true
+def is_tropical_atlantic(*, lon, lat):
+    lon = lon % 360
+    return ((lon > 295) | (lon < 20)) & (lat > -30) & (lat < 30)
+```
+*make bad usage difficult*
+
+::: {.notes}
+* First! make it easy to use it correct: handle larger longitude range
+* Second: make it harder to use wrong by adding "*" notation and assert latitude in range
+* Defaults must be **sensible**, i.e. lat=0, lon=0 would be nonesense
+:::
+
+## Interaction design: make bad usage difficult
+**Which button opens the door?**
+
+![](static/mpim_door_opener.jpeg){width="60%"}
+
+## Interaction design: don't lie
+
+**How do you get out?**
+
+![](static/mpim_door.jpeg){width="60%"}
+
+## Usability
+You get a result and the software is<br/>intuitive and easy to work with.
+
+## Usability: Ease of use {.leftalign}
+* intuition = function
+* standards
+* logical flow
+* well-structured navigation system or documentation
+
+## Usability: Efficiency {.leftalign}
+A result is there in
+
+* reasonable time
+* using reasonable resources
+
+# Information architecture
+
+## Definition
+> the art and science of structuring and organizing the information in products and services to support usability. - Wikipedia
+
+* the data model behind the interaction design
+
+# redesigning ICON output
+*(as a UX design example)*
+
+## 1. User research: what are typical plots that scientists make?
+
+![Analysis of ICON output typically loads way too much data](static/icon_plot_examples.png){width="60%"}
+
+## 2. Interaction design / Usability
+*previous (unstructured) output*
+```
+$ ls *.nc
+ngc2009_atm_mon_20200329T000000Z.nc
+ngc2009_oce_2d_1h_inst_20200329T000000Z.nc
+ngc2009_atm_pl_6h_inst_20200329T000000Z.nc
+ngc2009_lnd_tl_6h_inst_20200329T000000Z.nc
+...
+
+$ ls *.nc | wc -l
+  12695
+```
+
+## 2. Interaction design / Usability
+*optimizing output for the analysis*
+
+* provide an easy-to-understand overview
+* only few steps necessary to get the data
+* fast selection in time and space dimension
+
+```python
+ds = cat.ICONrunID.to_dask()
+t_hamburg = ds.tas.sel(...)
+```
+
+## 2. Interaction design / Usability
+*build on existing user knowledge: a single **dataset***
+![](static/icon_new_output.png){width="60%"}
+
+::: {.notes}
+* a dataset is a way to communicate information
+* "normalised" dataset without redundant info, e.g. exactly one info on time and space
+* balancing clarity and efficiency: sometimes duplicate information ("denormalised") can have a performance advantange, e.g. hierarchy in high-res model output
+
+* bad example: WALES flight altitude is time-dependent, but was a constant reference height. Bad variable name and misleading info
+* -> the structure and type of the data / array holds important information
+* best practice: limit only to the absolute necessary structure/info.
+* e.g. a dataset with u, v arrays is easy to understand. If there is also ws (redundant), it is unclear whether this is exactly the same as (u**2 + v**2)**0.5
+:::
+
+## 3. Information architecture
+* dataset holds pieces together
+* chunks increase performance
+* hierarchical dataset ("denormalized" to increase efficiency)
+* HEALPix grid supports chunks & hierarchy<br/>(but users had to learn something new)
+
+:::{.samller}
+*Further info on redesigned ICON output in [Tobi's EGU24 talk](https://tobi.pages.gwdg.de/egu2024/slides.html)*
+:::
+
+# The benefits of UX design
+
+## Why should we care?
+::: {.incremental}
+* lower risk of wrong usage and wrong results
+* it saves time and ressources and reduces information overload
+* a positive user experience can lead to collaborations and more fun :)
+:::
+
+::: {.notes}
+* example: when running ICON scientists usually don't look at the source code, but merely the config file. You need to trust that parameters do the right thing.
+:::
+
+# Specific guidlines in Earth System Informatics
+## Standards
+Standards are commonly known, they help guiding intuition, and also help implementors.
+
+* [POSIX](https://en.wikipedia.org/wiki/POSIX) (and GNU)
+* [UNIX return codes](https://stackoverflow.com/questions/1101957/are-there-any-standard-exit-status-codes-in-linux) (e.g. 0 - no error, everything else - error)
+* [CF conventions](https://cfconventions.org/)
+* coding styles, e.g. PEP8
+
+## File formats 
+use standard formats, preferrably machine readable
+
+* [YAML](https://yaml.org/), [JSON](https://www.json.org/), [netCDF](https://www.unidata.ucar.edu/software/netcdf/), [Zarr](https://zarr.readthedocs.io),  ...
+* YAML, JSON for machinereadable text file
+* netCDF, Zarr for binary data
+
+::: {.notes}
+* [NASA AMES FFI](https://espoarchive.nasa.gov/content/Ames_Format_Specification_v20#tth_sEc5.1)
+:::
+
+# Input
+
+## Separate code and configuration
+* **code** defines the algorithm
+* **configuration** defines the input / boundary conditions / settings
+
+## What is configuration? {.special}
+
+::: {.notes}
+* if you install microsoft word, your text (config) is not yet there. Default fonts are included, but if you have a special corporate design, you expect that to be separate
+* 
+:::
+
+## Hierarchy of configurations
+1. system config (`/etc`)
+2. user config (`~/.config`)
+3. project config (a file usually placed relative to current directory)
+4. environment variables
+5. CLI / API
+
+::: {.notes}
+e.g. when a software is used, such as `conda`, it searches through a hierarchy of places combines the configuration parameters
+:::
+
+## Parsing command line arguments
+* [Utility Argument Syntax Guidlines](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html):  
+  ```{bash} 
+  utility_name [-a] [-b] [-c option_argument] [-d|-e]
+      [-f[option_argument]] [operand...]
+  ```
+* [GNU style syntax following POSIX guidelines](https://www.gnu.org/prep/standards/html_node/Command_002dLine-Interfaces.html)
+    * single-letter Unix-style, e.g. `-i`
+    * long-named option, e.g. `--input` ([list of known options](https://www.gnu.org/prep/standards/html_node/Option-Table.html#Option-Table))
+    * recommended minimal options: `--version` and `--help`
+
+## Parsing command line arguments
+* standard command line argument parsers
+    * [`getopt`](https://man7.org/linux/man-pages/man1/getopt.1.html) - program for parsing arguments in shell scripts
+    * `getopt` exists also in C/C++ and many other languages
+    * Python: e.g. [`argparse`](https://docs.python.org/3/library/argparse.html)
+
+## Code {auto-animate=true}
+
+```python
+print("hello world")
+```
+
+## Code {auto-animate=true}
+
+```python
+name = "world"
+print(f"hello {name}")
+```
+
+## Code {auto-animate=true}
+
+```python
+import sys
+
+name = sys.argv[1]
+print(f"hello {name}")
+```
+
+## Code {auto-animate=true}
+
+```python
+import sys
+
+name = sys.argv[1]
+print(f"hello {name}")
+
+if "?" in sys.argv:
+    print("Pass in a name and I'll greet you.")
+```
+
+## Hands-on {.handson}
+:::{.smaller}
+
+Rewrite [safe_copy.py](static/safe_copy.py) to use intuitive argument names, then make it more intuitive.
+```python
+{{< include static/safe_copy.py >}}
+```
+:::
+# While running
+
+## Escalate Errors {.special}
+```python
+    if not os.access(args.outfIle, os.F_OK):
+        shutil.copyfile(args.sOurce, args.outfIle)
+
+    [...]
+```
+What could go wrong?
+
+## Raise exceptions
+* Programming languages use exceptions to communicate problems.
+* Functions further up in the call stack can then decide how to respond.
+* Uncaught exceptions generally lead to a program exit.
+
+## Try / except / else in python
+```python
+def do_things():
+    try:
+        res = do_risky_thing()
+    except ExceptionType as exc:
+        handle_exception(exc)
+    else: # optional, if no exception / ... in try:
+        work_with_result(res)
+    finally: #will be executed at the end, no matter what.
+        clean_up()
+    move_on_with_normal_flow()
+```
+
+## Clean up behind you
+* If you open a file, make sure to close it, even in case of an exception.
+
+* Use the [with](https://docs.python.org/3/reference/compound_stmts.html#with) statement in python.
+
+## Write decent error messages
+* Tell what happend why and where
+* Exit with non-zero exit code
+* Make sure things can't continue (if that's relevant)
+   
+## Hands-on {.handson}
+Take safe_copy.py and add useful error messages and return codes.
+
+# Output
+
+## stdout and stderr
+
+* stdout is for ouput somebody else might want to process
+  `grep bash /etc/bashrc | wc `
+* stderr is for *error* messages  
+  `grep bash file_that_does_not_exist |wc`
+
+## Datasets
+* a dataset is a way to communicate information
+* clarity in the dataset structure is clear communication
+* group things together that belong together
+* follow standards
+
+## Dataset normalization
+(normal as in orthogonal, not *the usual mess*)
+
+* Idea: Only provide information that is not already contained otherwise.
+* Keeps your dataset clean
+* Balance with the effort it takes to compute information that you optimized out.
+* Provide scalars as scalars, not as 3D fields / ...
+* Aggregation levels can be worth the extra data / effort
+
+# Take-home: take a user's perspective! {.special}
+
+* use standards
+* be consistent and intuitive in your style
+* "Don't lie" :)
+
+:::{.notes}
+* don't return a result if an error occured
+* don't change user input within the code without communicating the change
+:::
+
+# Further reading
+* Edward Tufte: *Beautiful Evidence* (and other books)  
+  [UHH Library System](https://katalogplus.sub.uni-hamburg.de/vufind/Record/80685877X)
+* *Dead programs tell no lies* (Topic 24 in Ch 4 of *The pragmatic programmer*  
+  [UHH Library system](https://katalogplus.sub.uni-hamburg.de/vufind/Record/168729271X) | [MPS ebooks](https://ebooks.mpdl.mpg.de/ebooks/Record/EB001950880) | [German ebook via UHH](https://katalogplus.sub.uni-hamburg.de/vufind/Record/1755846843))
+* *Defensive Programming and “Fail Fast”* (in Ch 13 of *Fluent Python*).
+* *with, match, and else Blocks* (Ch 18 of *Fluent Python*).
+
+# What's your UX with this lecture series? {.special}
\ No newline at end of file
diff --git a/lectures/user-experience/static/gui_example.png b/lectures/user-experience/static/gui_example.png
new file mode 100644
index 0000000000000000000000000000000000000000..37441263c115115cfffde7d900e2cb64a2b4ef60
Binary files /dev/null and b/lectures/user-experience/static/gui_example.png differ
diff --git a/lectures/user-experience/static/home_page_example.png b/lectures/user-experience/static/home_page_example.png
new file mode 100644
index 0000000000000000000000000000000000000000..e23b37b0d61415fb1c65c2c67be5c4913308835e
Binary files /dev/null and b/lectures/user-experience/static/home_page_example.png differ
diff --git a/lectures/user-experience/static/icon_new_output.png b/lectures/user-experience/static/icon_new_output.png
new file mode 100644
index 0000000000000000000000000000000000000000..47606e3546d64eeb424aef3a4e3d46de74cde879
Binary files /dev/null and b/lectures/user-experience/static/icon_new_output.png differ
diff --git a/lectures/user-experience/static/icon_plot_examples.png b/lectures/user-experience/static/icon_plot_examples.png
new file mode 100644
index 0000000000000000000000000000000000000000..a1afbd2bc1128a2246947086bcb701bb47db98b0
Binary files /dev/null and b/lectures/user-experience/static/icon_plot_examples.png differ
diff --git a/lectures/user-experience/static/mpim_door.jpeg b/lectures/user-experience/static/mpim_door.jpeg
new file mode 100644
index 0000000000000000000000000000000000000000..157cddda282c4ce6f744dc70937189dbab2642f2
Binary files /dev/null and b/lectures/user-experience/static/mpim_door.jpeg differ
diff --git a/lectures/user-experience/static/mpim_door_opener.jpeg b/lectures/user-experience/static/mpim_door_opener.jpeg
new file mode 100644
index 0000000000000000000000000000000000000000..c832b751704aaa57c4d460c258d2d50677e791af
Binary files /dev/null and b/lectures/user-experience/static/mpim_door_opener.jpeg differ
diff --git a/lectures/user-experience/static/safe_copy.py b/lectures/user-experience/static/safe_copy.py
new file mode 100755
index 0000000000000000000000000000000000000000..b64e2b29ae652381420a4f14d2027d1d18146d65
--- /dev/null
+++ b/lectures/user-experience/static/safe_copy.py
@@ -0,0 +1,23 @@
+#!/usr/bin/env python
+
+import argparse
+import shutil
+import os
+
+
+def parse_args():
+    parser = argparse.ArgumentParser()
+    parser.add_argument("-I", "--outfIle", help="Where to copy")
+    parser.add_argument("-O", "--sOurce", help="Source file")
+    args = parser.parse_args()
+    return args
+
+
+def main():
+    args = parse_args()
+    if not os.access(args.outfIle, os.F_OK):
+        shutil.copyfile(args.sOurce, args.outfIle)
+
+
+if __name__ == "__main__":
+    main()