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

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-unlit

README.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:// and wss:// (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