Developer Experience Matters
ZuriHac @ 2024
Domen Kožar @ Cachix

How do we create software that's pleasant to use?
Empathy!

Cognitive overhead
fortune telling
biased towards finding a solution
context switching
making decisions
unintuitive interface
complexity
Declarative
Developer Environments
Using Nix
devenv.sh
{ pkgs, config, ... }: {
env.GREET = "determinism";
packages = [
pkgs.ncdu
];
enterShell = ''
echo hello ${config.env.GREET}
ncdu --version
'';
}$ devenv shell
hello determinism
ncdu 2.3{
scripts.nushell-greet = {
exec = ''
def greet [name] {
["hello" $name]
}
greet "world"
'';
package = pkgs.nushell;
binary = "nu";
description = "Greet in Nu Shell";
};
}devenv.sh
{ pkgs, ... }: {
packages = [
pkgs.mkdocs
];
processes = {
docs.exec = "mkdocs serve";
};
}$ devenv up

$ devenv up
$ devenv test
{ pkgs, ... }: {
services.postgres = {
enable = true;
package = pkgs.postgresql_15;
initialDatabases = [{ name = "mydb"; }];
extensions = extensions: [
extensions.postgis
extensions.timescaledb
];
settings.shared_preload_libraries = "timescaledb";
initialScript = "CREATE EXTENSION IF NOT EXISTS timescaledb;";
};
processes.docs.exec = "${pkgs.mkdocs}/bin/mkdocs serve";
enterTest = ''
wait_for_port 8000
curl http://localhost:8000 | grep "Hello, world!"
'';
}
{ pkgs, ... }: {
languages.haskell.enable = true;
languages.haskell.package =
pkgs.haskell.compiler.ghc9101;
services.ghcid.myexe = {
package = "mypackage";
components = [ "lib" "exe:myexe" ];
args = [ "serve" ];
stack.enable = true;
};
pre-commit.hooks = {
ormolu.enable = true;
cabal-fmt.enable = true;
};
}devenv.sh
- Develop natively
- Deploy containers
- 80.000+ packages
- Write scripts
- 50+ supported languages
- Define processes
- Reuse services
- Run tests
- Enforce git hooks
devenv.sh
Haskell
Developer Experience in
executable readme
main-is: README.lhs
build-depends: base, mylib
ghc-options: -pgmL markdown-unlit
build-tool-depends: markdown-unlit:markdown-unlitREADME.lhs typechecking
myprogram.cabal
Convention over Configuration
GHC20XX
The extension is (mostly) conservative: All programs that were accepted without the extension remain accepted, and with the same meaning. We prefer to take this as a strict criteria, and grant exceptions on a case-by-case basis.
No more extension hell!
SDKs
High-level APIs
cachix/typed-websocket
- Splits up receiving and sending into separate threads
- Wires up Ping/Pong for Client and Server
- Uses channels for sending/receiving messages
- URI based API supporting
ws://andwss://(for the client) - Simple interface between client and server send/receive types
- Retries connection indefinitely for the client automatically using Stamina
- Handles graceful shutdown for the client
Exceptions
Writing practical software in Haskell requires unnecessarily deep knowledge of the exception system.
No escape from exceptions. Pervasive use in critical library infrastructure.
ExceptionInLinkedThread is an async exception. According to safe-exceptions, this should not be recoverable. In practice, these are often just sync exceptions that were run concurrently. Example: concurrent HTTP requests with retry handling.
cachix/stamina.hs: Retrying for humans
Thank you
Developer Experience Matters
By ielectric
Developer Experience Matters
- 97