How many spoons does your environment cost: Broken demos & human element - Dawn Wages
Watch on YouTubeVideo summary
Dawn Wages opens her presentation by introducing the concept of "spoons" as a metaphor for finite cognitive energy, drawing parallels to Spoon Theory originally used in chronic illness communities. She argues that software development environments, particularly in Python, impose hidden costs on this limited energy reserve that are often overlooked. Instead of viewing environment setup and maintenance as free overhead, she suggests that every conflict, broken dependency, or hardware mismatch consumes valuable spoons, leading to interruptions in attention and intention. This cognitive load is shared across teams, meaning that when one developer struggles with a failing environment, it impacts the collective capacity of the group to focus on actual coding tasks rather than infrastructure troubleshooting.
To illustrate these invisible costs, Wages demonstrates several real-world scenarios where environments fail due to mismatches between hardware assumptions and reality, naming conventions, or package management tools. She shows code that fails immediately upon import because a library like CuPy is incompatible with Apple Silicon, or when a Linux-compiled extension cannot find its dependencies on macOS due to different dynamic linker paths. Other examples include Docker containers running out of memory during emulation, timezone issues persisting across restarts, and conflicts between Conda and Pip installing packages in different locations. These technical failures force developers to constantly switch tasks, reload mental models, and restart workflows, which research indicates can take an average of twenty-three minutes just to regain full focus on the original task after each interruption.
Beyond the technical layers of abstraction—from firmware and hardware up through operating systems, interpreters, and application intent—Wages emphasizes the human element in how we process these failures. She explains that our working memory has structural limits, and when infrastructure problems occupy this space, it reduces capacity for complex thought and creativity. The presentation concludes by highlighting emerging ecosystem efforts to mitigate these issues, such as PEP 751 for lock files, new wheel formats, and container standards like OCI, which aim to improve reproducibility. Ultimately, she advocates for sustainable autonomy through deliberate ownership of one's environment, encouraging developers to build resilience by understanding the full stack while balancing efficiency with the mental load required to maintain control over their tools.
Read the full video transcript
[applause]
>> Hello everyone.
It is a pleasure to be here. This is the
second time I'm giving this
presentation. And so, what we're in in
for is going to require some audience
participation. You're going to see some
failing code five times exactly. And
then when it fails, I'll let you know
when it happens, but I would love for
you all to cheer for me. Is that okay?
Okay. All right, so let's do a practice.
Yeah? Okay, let's do it one more time. 1
2 3
Oh, okay. I like this. Okay, this is
really great. All right.
Uh so yes, my name is Dawn Wages,
she/her pronouns. Um and it is an honor
to speak with you all today. This is an
experimental version of a talk that I've
given about a half a dozen times.
Um and I'm adding in a human element to
the abstraction and process
considerations of your Python
environment. This is going to cover why
it's so hard to build and maintain
software in any environment, but
especially specifically a Python
environment. I'll have a moment of
vulnerability. Feel free to laugh at me,
the last audience did. Um when I came
into product aligned work a number of
years ago and I was working on VS Code
with some of the brightest and most
thoughtful Pythonistas I've ever met. Um
I said, "Well, are Python environments
even that hard?"
Yeah, yes. That's where the laugh goes.
Yes. Um and it was a resounding yes. Um
and almost a duh. And I've thought a ton
um over the last um maybe five or six
years that I've been doing this on why I
thought it could have been easy despite
it being incredibly hard. One, I
internalize failure. When my
environments broke, it was me. It wasn't
hard. I must have been dumb or lazy or
didn't read the right docs, or tired, or
something.
Uh two, I came into programming in
around like 2016-ish
area, and it's a time period where
we were aggressively solving for some of
those considerations. Really trying to
make the the environment and getting
started that 101 level experience of
development much easier. I mean, I my
first uh deployed Python site was
through Django Girls, and the if you've
ever been through the Django Girls
documentation, they really step you
through the process. And also, number
three, I've always viewed programming as
a team sport. I came through Django
Girls, I've always worked in lovely
teams. I was lucky enough to start
programming before the um remote um
options of in of development, and so I
had a a team member on the next cubicle
to talk me through and help me through
processes.
So, game time. As I mentioned, in this
talk, we'll have a small game. I'll be
running those scripts that are designed
to break on one side of the screen, and
when you see it break, I'd love for you
all to cheer.
So, we're going to hack our brains.
Let's give it another try. I already
know you did it before, but let's do a
woo.
>> Woo!
>> [cheering]
>> Yay! Thank you.
Um I lead community and developer
relations at Anaconda. I also co-host a
podcast with Luciana Bud, and it will be
returning from hiatus this summer. Um
and you also may find me on the internet
experimenting with local-first AI and
ML.
So, who all, and I guess a raise of
hands, it's a bit dark, but I can see
the hands. Who's familiar with spoon
theory?
Okay, cool. But, it's enough of the
audience for me to kind of go through
it. It first was coined by Christine
Misia Mis Ren
Misia Randino, uh a real originally
about chronic illness, and it maps
disturbingly well to the hidden attacks
of cogni- cognitive works, and so that's
what I'm going to be uh referencing
today. The core insight isn't just
energy is finite, which is what it
covers, but it's that different tasks
cost different amounts, and the costs
aren't always visible, and you can't
always choose when and how you spend it.
There is no connection to why a spoon
versus another object measure. I got a
hallway track conversation last time of
like, why spoons? And they were thinking
about it the whole presentation. Just to
close that loop, spoons are what was on
the table when Christine was describing
what it was, but it describes a finite
number of what you of your energy in any
given time period.
And from this terminology, spoony grew
into an identity term for
self-identifying as someone with a
chronic illness. Every task costs
spoons, and you can't push on willpower
alone.
So, we're managing with limited energy,
and your environment setup is almost
entirely miscategorized as free
overhead. When a developer sits down to
work and hits these conflicts, an
ambiguous path, a broken environment,
they're spending spoons on something
that was supposed to already be done,
and that cognitive cost isn't just a
technical challenge. It's an
interruption in attention and intention,
and that is shared amongst your team
members. So, the goal of this
in the audience that's for this
presentation is for team leads who are
managing environments, for individuals
who may internalize failure like I do,
and for people who are builders, who are
maintaining some of these systems that
are interruptible, and they have these
pending tickets. They know that there
are some bugs,
and to prioritize or understand the and
have empathy for the bugs that they're
fixing.
So, sometimes you're a developer, and
you're starting your day already with a
limited amount of spoons. Sometimes
we're not lucky enough to wake up fully
refreshed.
You spend the cognitive effort at the
beginning of the day to write down your
to-do's.
Maybe you understandably forget some
personal to-do's in your first draft.
And then you have your tax task that
will be ultimately need to be expanded
or adapted.
When you fill your cup and you eat
breakfast, you stretch before you sit
down, maybe your spoons go a little bit
back up.
And then you start your day.
Let's start with our first broken code
and then we'll continue back on to our
personal experience.
So,
All right, this is going to be a first
one. Let's get bigger.
Okay.
All right.
So, what's happening here?
Uh we have the application we're running
into conflicts in our package
dependencies. So, CuPy is a NumPy
compatible array library in uh CUDA GPUs
and it does not support Apple Silicon,
which is what I'm running this on. If
your code base uses CuPy for GPU
accelerated NumPy operations and the
Apple equivalent
is going to be the Apple ML framework
NumPy compatible MLX or torch with MPS
backend or JAX with metal plugin.
Regardless of the in-depth scenarios of
this, none of these are drop-in
replacements. There are migrations
required auditing CuPy call CuPy call.
All right.
And so this is going to be hardware
understanding the hardware assumptions
versus hardware realities. These are
going to be failures at import time and
device check time and the code is going
to be written assuming one thing but
actually finding another.
So, let's return back to our our your
You knocked out an issue between this
and the last uh last slide.
And your list gets heavier, and you try
to remind yourself to take care of
yourself. Drink water. Mentally prepa-
prep- uh prep. Don't forget lunch.
And your day just somehow continues to
get even more hectic.
And without reprieve, you just make it
through.
But even what you have on paper isn't
all of it. The things that require
feedback.
Uh sometimes you have to stop and change
gears and restart.
And again, more context switching.
And you may ask for help on on your on
your teammates' terms, and by the 2:00
p.m. slump, you're almost empty.
So, here's what we're going to cover
today. We're going to talk about why
environments fail, and then we're going
to talk about why we fail.
Uh let's do another demo.
All right.
Okay, what's happening in this one?
So, this one's about dlopen. The
extension was compiled expecting to find
libgiomp.so.1.
These are .so extensions in the pip lib
directory, which is exactly where pip
would put it on Linux, but I'm on a Mac.
So, conda doesn't ship uh this extension
because Mac OS doesn't use that name,
and so we have what is a naming
mismatch.
The package installed, the pip was
reported success, and the wheel landed
in site-packages, but the failure only
import um appears at import time. We've
missed the first cheer because I was too
deep into my um into my code, but here
we go. Failing code.
>> Woo!
Woo!
>> Thank you. I really should have done
that the first time. It really helps.
So, thank you.
All right. So, let's go keep going back
to the Prezi. So, why did it fail? This
was a naming mismatch. No amount of
searching can fix a naming mismatch
and an extension compiled on Linux with
GCC hard codes its package name at its
run time dependency, but when you're
running on Mac, which is what I'm doing,
uh the dynamic linker searches every
known path and can't find it. So, if it
finds a problem at run time, not before,
and this is just a
factor of different conventions and
operating systems.
All right, so let's talk about the two
different ways that you are inter-
interacting with your two different
paradigms that you're interacting with
your with your environment. First, I'll
cover and this is going to be the
majority of it. I'll talk about the
layers of abstraction in your
environment and then I'll also talk
about process.
So, here are the seven layers that I've
articulated as the the process. So, you
start really low level, physical
firmware. You have your hardware
abstraction.
You'll have your process and your
operating system interface, some of
which I've already mentioned before and
you've already seen this kind of
abstraction layer.
Environment isolation, then your package
dependency, your run time interpreter,
and then your application and your user
intent. This serves as a model for
creating resilient environments that can
traverse this stack.
And then the process, right? So, this
may look really familiar to just any
iterative ideation process as you're
building, you're planning, you're
designing, you have your build sprint,
you're testing, and then hopefully
you're releasing.
But, abstraction can be messy.
Extensions can bypass run time, layers
can be substituted for each other, and
there's also novel developer hardware.
And your process can be messy. This is
more like what my process looks like.
You plan and then you plan again and
then you design and then you turn it up
on its head.
Maybe you start building and then you
stop and you test all the way through or
something and then you got to start
again. And the process is not all cut
and dry. We have false start starts and
stops, designs and redesigns, testing in
imperfect ways, and ecosystem
considerations and releases.
So, let's go through some of these
abstract the layers of abstraction.
The application user intent is what the
developer is meaning to do. The layer
where the correct algorithm fails
because the environment was written for
that environment and it could have
silently diverged.
The one time interpreter, this is where
you have that translation layer between
the Python syntax and your machine
instructions. So, this is CPython, PyPy,
Py
313T,
this is all different kind of flavors
and variations of what's going on in
your runtime, your language in your
runtime. Um but each make different
promises about your global interpreter
lock, for example, uh your garbage
collection, or what C extensions that
can say that can be safely assumed.
And if you want to go deeper into this,
we'll just do a pretty quickly. I'll run
through some of these, but you also have
what's happening during that runtime
interpreter layer. And this is how
Python works. CPython does seven several
things in sequence. It'll parse your
code into an abstract abstract syntax
tree that compiles tree into byte code,
and then have lower level sequence of
instructions stored stored in your PyWI
PyC files.
Uh then you'll execute that byte code in
the Python interpreter,
uh excuse me, and the Python virtual
machine in a stack-based interpreter
loop written in C. And that pipe byte
code is not machine code, it never runs
directly on your CPU.
And that CPython uh reads each byte code
instructions and executes the
corresponding C code for it.
And then if you're looking at where that
C API is, that's going to be where your
source level API for if you are using
other languages and then you have
underneath that the ABI which is your
binary
level which will define function
signatures and macros, headers and stuff
and in more.
Okay.
Um next we'll talk about package
dependencies. So this will be that
social contract between your library
authors and the versions that can
coexist um in your solving. So this
breaks when the contract is implicit or
under documented or written for hardware
targets that are not yours.
In your environment isolation, so this
would be your virtual environment, your
venv or your conda environment. That
bounder boundary says this project's
dependencies are not that project's
dependencies. venv, conda, docker all
attempts to draw lines around uh that OS
it doesn't naturally uh that the OS
doesn't naturally enforce.
So let's go to another breaking code.
No, not yet. Almost, almost.
>> [laughter]
>> But thank you. I love how you're ready.
So we're talking about mounting in this
one as well. So this one's going to be
about a docker fail. Um your platform is
going to be mismatching in this one.
We're unable to allocate memory. So at
one time when you are uh
um
defining how much memory you have and
you're running it on an emulation, um it
may run slower and you may run out of
memory. Also docker does not inherit
your shell environments and in this
particular one, timezones are hard.
Woo!
[cheering]
Thank you. Thank you. Y'all are on it. I
love it. Okay.
So this one's more about emulation. So
the failures that are discovered either
after the fact which would be like wrong
data or external observations or a
downstream issue like time zone like set
in setting US UTC for example uh will
create problems later and not at the
installation installation time.
Let's keep going. So, we'll talk about
process and OS interface. So, this will
be more about file descriptors, local,
signal handling, memory limits, mount
points. Um the OS enforces constraints
that the developer didn't know existed
until they're violated. And we showed an
example of that before.
In your hardware extraction, we're
getting really low level now. This is a
layer that pretends all of the compute
is equivalent. So, if you're using CUDA,
MPS, like I've mentioned before, it's
metal, and CPU are all not the same
thing and they have different names. Uh
but they have different and they have
different D type support. So, this means
that there is different precision
guarantees and different failure modes
at the same API surface.
And then finally, you have the lowest
level, which would be your actual
hardware or your firmware. The layer
that doesn't negotiate. This is where
you have I have my Apple silicon that
doesn't have any CUDA on this on this
particular machine. Uh the disk is full,
for example, or you have network cable
that is unplugged somewhere, or um
or some other hardware-related uh
integration. And no amount of correct
Python is going to resolve resolve the
fact that you have differences in the
real world in the physical world.
And this is your example test
environment. This is a process of what
you're going to build your widget.
And my build process, as I mentioned, is
a lot different. I'll have an ideation
phase. I'll have a plan phase, a test
phase that are written against
environments that may or may not exist
or uh release flow that I'm assuming,
but does not always match reality.
Uh this will be the last demo.
And no almost done. Almost done.
Still.
>> All right, so what's happening here? So,
we're seeing some dependency conflicts
and the wrong interpreter. Uh we also
see conda and pip are package uh
clobbering our packages. For example,
conda and pip are putting the packages
in different locations of your machine
when you're when when installation. And
so, when it goes to look for it, it
doesn't know which version is supposed
to be uh which. Although, there are some
tooling that's coming. Find me in the
hallway track. I'll talk to you about
conda PyPI. We also have in here a
broken editable install. Uh so, if
you're have a mono repo, for example,
and you're um pushing your code and the
editable package is not there, it's not
going to know until runtime. Um and then
also in this particular one, we all have
an architecture mismatch.
Uh cheers. Woo!
>> [cheering]
>> I really like that. I need to build this
into every one of my presentations. It's
so good. Um
Great. Thank you so much. So, that's the
last of the demos. But, I want to finish
with uh So, this is what happened. When
it's installed, what installed and when.
But, now I want to go into just the
human focus. Why do we fail?
Well, uh here's an example of where our
memory model may be. So, there you have
cognitive limits and I want to explain a
little bit more about them. So, the
critical point is capacity. The central
executive uh has a fixed ceiling.
You also have your phonological loop. Uh
these mean that there is a structural
uh constraint on your cognitive system.
It holds sound based off of energy
information before it decays. Right? So,
you're going to have uh a point where
you first learn and it's going to be
most crisp in your mind and then it will
continue to decay over time.
You also have your visual space um
spatial sketch pad. So, this is visual
and spatial memories respectively. So,
if you are one of those mind palace type
of people, this is also a hack into your
brain on how you uh can build your
memory models. And then we also have the
episodic buff um buffer. So, when you
have limited capacity, uh you can have a
temporary store that integrates across
memory and components. And at the bottom
of the screen, I also have uh research
that uh describes some of these.
The spoon is not running low because
you're tired or frustrated. Uh it's
running low because the working memory
architecture that enables complex
thought is occupied with infrastructure
that was supposed to be invisible. And
this memory model kind of helps us kind
of go through some of these cognitive
limits we have.
One of which I also want to talk about
switching tasks. So, every time an
environment breaks, it forces a task
switch uh switch, which is a goal of
reconfiguration cost and a rule
activation cost also load, which means
loading the procedural rules for that
new task. Um I'm calling out Meyer and
Rubenstein's 2001 paper specifically, um
but I also have Gloria Mark's uh paper
at the at the bottom just to kind of uh
notate where the references are. Meyer
and Rubenstein's paper demonstrated the
costs were not reproducible to a simple
surprise or confusion. They persisted
even when participants knew exactly when
and what they would switch to. Gloria
Mark says, which is the most cited
finding, that it takes on average 23
minutes to fully return to a task after
interruption.
An average task that includes the entire
chain of secondary tasks and
interruption triggers. So, just kind of
giving yourself grace that interruptions
actually do and research-wise um really
slow you down.
And then the last is miscategorization.
So, as I alluded to in the very
beginning, this work is not free. There
is invisible labor in the environment
tasks that are um showing up in your
development workflow. So, setting up a
Python environment reads as I've done
this before and gets budgeted as maybe 2
hours. But, the fact remains that the
environment has different OS constraints
or different package ecosystem or
different hardware target. None of that
invisible planning time is factored in
and the estimate for that categorization
environment setup is not specific for
whatever your use case is. So, giving
yourself grace and understanding that
your team is also going to have to
traverse and traverse this as well, good
documentation helps, but understanding
that there is mental load and
categorizing it as not invisible until
it's too late.
And we're rounding out the end of this
presentation, so I really want to kind
of guide you to what the ecosystem is
doing about some of these issues, but
really you'll find a lot of the
decisions that are happening to mitigate
these considerations in the issue logs
of a lot of these open source projects.
I want to guide you to uh PEP 751, which
is lock files. Um there's wheel next
that's happening in the ecosystem.
There's conversations of having a new
wheel format and also the infrastructure
that supports it. Much of that is
happening through the uh new packaging
council and the Python Software
Foundation and PyPI. Um there's also
container works that's happening as
well. Docker and OCI containers address
environment reproducibility as well um
and they are um solving some of these
problems. And then also PEP 723, which
is uh metadata that's also going to show
up in the packaging council work.
So, last but not least,
I want to talk about uh the sustainable
autonomy through deliberate ownership.
So, by taking control of your
environment, I want to give you the
agency to be able to uh understand at
the beginning of the task or while
you're in the middle middle of it and
you're like me internalizing some of the
failure that you may see on your
console, that you're able to build
strength and it comes from balancing
that efficiency and iteration while
maintaining ownership in the systems
that you're depending on.
Uh thank you so much for your time. I
appreciate it.
>> [applause]
[applause]
>> Okay.
I think I used all my time, so I don't
know if I have time for questions.
>> Um
>> But I can probably do them out in the
hallway. Which is
>> Okay. Uh thank you, Dawn. It was an
amazing presentation. Thank you so much.
Uh we have some time for a couple of
questions, so if anyone wants please
proceed to the microphone.
>> Hi Dawn, I'm Vlad. Thanks so much for
your talk which I really enjoyed. Um I
really liked how you appointed out all
the different levels of software
abstraction and I think it's interesting
how, you know, usually when we're
building the apps that we're building,
we're rather focused on the higher
levels of abstraction, but sometimes
things can go wrong in the lower levels
of abstraction and we're not prepared.
However, familiarizing yourself with
those lower levels can also take up a
lot of spoons, like you said. And so I
was wondering what your thoughts are,
what your approach is in terms of
educating yourself about those lower
levels so that you can be prepared
without uh
you know, in a realistic way that is not
like a tremendous amount of effort.
>> Yeah. Um so I'll start with maybe my
enterprise context if like I'm doing it
at work. Many times they issue the same
hardware to you and your peers, so you
can ask them what they're running on and
if something like looks familiar, right?
So I moved over from
Windows to Mac recently and I've been a
decades-long Windows developer and you
know, got really I really enjoyed WSL 2.
You can complain in the hallway with me
if you want, but yes and
and so it was really a learning curve to
figure out what tools mapped from one
operating system to another and it's a
big jump to going from Mac to Windows.
Uh
tools on how to do it. I just kind of
doc I've continued to document, right? I
have this running wiki page on all of
the equivalent
Oh, I can get this in tmux and I had
this in here and maybe I can do this to
hack some new thing. Um I don't have an
easy description or like easy path, but
documentation asking my peers and
leveling in context or connecting in
context where you have the shared
resources is really helpful.
I don't know if I answered that well,
but it's hard.
>> No, it's good to hear your experience
with it. Thank you.
>> I think there's time for one more
question if anyone wants.
>> [snorts]
>> Okay, cool.
Okay, thanks again Don.
>> Thank you.