Alright! Hi everyone! Happy EmacsConf!
I´m so excited to be here.
It's surreal to be part
of the conference itself,
in addition to being a viewer.
EmacsConf is like Christmas to me,
and I'm so excited when it comes around every year.
Today, my talk is on a programming methodology
that I've created, discovered, stumbled upon.
I call it "Bookclub Tapas."
Before we get into that,
let me introduce myself.
My name is Maddie Sullivan,
and my pronouns are she/her.
I go by the handle ElephantErgonomics,
which is shortened down to ElephantErgo in the IRC.
You can reach out to me after the talk
for questions, comments,
or just to say hello by reaching out
to hello@ElephantErgonomics.com.
So this software development strategy I found,
it's inspired by literate programming and Agile.
So what exactly is Bookclub Tapas?
Bookclub Tapas is a conversation
that you have with yourself.
It's a log and a ledger,
of your intentions, hopes, dreams,
and what you've learned
over the course of development.
Bookclub Tapas is an oracle
you can consult
about the state of, and the strategies behind,
your development process.
Bookclub Tapas is also a peer programming partner
that helps you decide how to best put forward your efforts
and how to best pull together
what you're working on.
Bookclub Tapas will also help you to understand
how to tailor scope to your needs,
and how to have the best parts
of your program shine through clearly.
Bookclub Tapas consists of two parts:
Bookclub and Tapas,
but what does that mean exactly, though?
Bookclub is a reverse literate development strategy.
Bookclub is a time for you to write,
and then read and reflect.
It's like a Bookclub,
but it's for your program.
Instead of inserting narration into your code
to narrativize what you are accomplishing,
you are instead inserting snippets of code
into your narrative to make it come alive.
So, what are we narrativizing, exactly?
What sort of story are we telling?
Bookclub is the story of you, your program,
and how your efforts are allowing your program
to come into the world.
Software developers naturally have to wear
a lot of different hats,
and take on a lot of different roles.
We apply ourselves into a lot
of different contexts.
We do research, interface architecture design,
mathematics, philosophy.
We take in the world around us
and then build abstractions to model it.
We translate the abstract
into the concrete,
and then when we're trying to teach software
how to be "smart,"
we translate the concrete
back into the abstract.
I can't help but feel like so much of
what makes software development difficult
is just trying to remember
and keep track of everything.
We have to try and remember
so many different implementation details.
We have to remember how our own code works,
how the API of our dependencies work,
how relevant real-world constraints behave,
what the standards lay out,
and how our data structures are laid out.
When we're debugging,
we simultaneously have to remember
how our program is currently behaving,
as well as how the program ought to behave
in order to get a chance
to reconcile that gap.
It's honestly all way too much.
We need a ledger of what we're actually doing
in order to stay sane.
I think a really effective way to
make sense of things that are complex and important
is to narrativize them,
to turn them into stories.
This is a strategy that humans have been using for a long time.
Mnemonic devices, metaphors,
and drawing parallels
are all different ways of doing just this.
Telling stories helps us to understand
things that are big and complex
by grounding them in our own experience
and making it fit into our scale.
So because the way that everyone
naturally tells stories
is going to be a little different,
because the details that strike us
as important and worth focusing on
are going to be different for different people,
I'm not going to say
that there are hard and fast rules
about how Bookclub "should work,"
because how it "should work"
is however it best fits your needs.
Different people and different projects
have different backgrounds and mindsets.
And I don't think it's my place to say
what strategy is correct as a universal law.
You know, because Bookclub Tapas is, after all,
just something I've sort of stumbled into.
Bookclub is intrinsically ad-hoc.
My providing a prescription of strategy
is basically going to begin and end with the idea
that you write a reverse-literate document
that illustrates how you've gone
about writing your program.
All of that being said,
I'm going to talk about
how I've laid out my book club files
and why I think this is a solid place
from which to get started.
So my stock off-the-shelf suggestions
for just getting started
is to have sections for: our overarching goal,
our development goals,
a place for scratch work, a test suite, research,
and then finally sections for variables,
functions, and macros.
So we have our starter kit sections.
How do we go about using them?
How do we get started?
Well, we write them, you know,
out in our org document,
but then what do we do?
We start by writing what we know.
We have a spark, a vision.
We had the beginning of an idea
of what we wanted our program to do.
Alternatively, maybe we had
a client lay our goals out.
Either way, we have some idea
of how we want our program to be shaped.
Let's start by writing that down.
What are we trying to do?
What is our goal?
After that, we're probably wondering to ourselves,
"Okay, we have our goal,
but how do we get there?"
That's when we start writing
our development focuses.
If we have bursts of intuition
about what functions to write,
questions that we want to answer through research,
we start enumerating those every time they hit us.
Our goal is to write them
all down in a checklist
in order to turn them from daydreams
into courses of action.
If we aren't having development focuses
hit us right away, that's okay.
If we just stare at the goal for long enough,
I think it's inevitable
that the muse will speak,
and we'll get a clear lead
on a path forward.
So now what?
Now that we have our development focuses,
we want to go ahead and create
the rest of the headings for ourselves
so we can act upon them.
We go ahead and write the rest
of the file's structure ad-hoc
in a way that will serve our needs for now.
If it's not fitting us well later on,
we can just go ahead and change it.
There's no pressure.
That's the beauty of having this
all be in a plain Org document.
If we're doing something consistently,
we probably want to have a heading for it.
We'll go ahead and create homes
for our variables, our functions, our macros.
We'll want to create a spot for scratch work
to sort of like stretch our legs
and lament in a stream-of-consciousness
sort of format about how
a particular piece of design ought to work.
Basically, any time we wear a different "hat"
or we take on a different "role" as a developer,
it's worth considering creating a category for it.
The best way for us to figure out
what headings to fill in,
and how to fill them in,
is to just go ahead and act upon our development goals.
If we have a question we want to answer,
we'll want to create a Research heading
so we can go ahead and have a spot
for scratch-work for reasoning things out.
If we want to write the first draft
of a function we want,
We'll want to create a heading for functions
and then a sub-heading for that function in particular.
So now that we've filled in our sections,
what do we do now?
Our idea for a program
has been turned into a story,
but what does that actually get us?
To me, a lot of what's exciting about Bookclub
is that novelization goes in
and a peer programming partner comes out.
As we loop through reviewing our document,
as we scan it up and down,
we're able to engage in conversationality
with our past self because of how verbose
we've been in our notes.
We can ask our past self questions,
and get back answers.
We've turned our past self
into a peer programming partner.
If we're wondering what to do next,
we can check our Development Focuses.
If we're wondering how something works,
we can read documentation
embedded in our function drafts,
or we can read the outcomes of tests
that we've performed in our research.
We can ask ourselves questions and get answers.
Some of what's most exciting
about peer programming to me
is having fresh perspective
and alternate context.
We have a fresh set of eyes
on the program that aren't our own,
and with that set of eyes
comes someone else to share the burden
of trying to remember everything.
With Bookclub, instead of having
a peer programmer that exists in physical space,
we have one that's, to get all sci-fi for a moment,
reaching forward towards us
from backward in time.
We're asynchronously working
with our past selves
as an equal-role collaborative
partner in development.
We have their perspective,
their fresh memories of the code as it was written,
and their focus on what was worth worrying about
at a different point in time.
We can ask them questions and get answers.
We can ask them questions like,
well, "What do I do now?"
"How does this data structure work?"
"What types does this third-party library take?"
By asking these questions,
I can even stay fresh
on development progress
that I last touched months ago.
It's really easy to duplicate work,
forget how things work,
lose track of priorities.
Bookclub helps keep us focused,
it keeps us accountable,
it even keeps us company.
One of the most immediately useful things about Bookclub,
in my opinion, is that we immediately have
a list of actionable items.
Every time I have a little pain point,
I go ahead and write it down,
and I write down all of the things
that would be nice to have done someday.
So you might be wondering,
and it's fair to wonder this,
isn't this effectively just the GitHub issue model?
We're listing out bug requests,
issue requests, feature requests.
It's not exactly a new idea,
and it's pretty intuitive.
I think the important consideration here
is that having really formalized apparatus
for entering in our thoughts
can be an unnecessary source of friction.
Bug listings don't tend to be
a great fit for daydreaming
or verbose considerations of philosophy.
Bug listings tend to be reserved
for catastrophes.
I feel like a lot of the tooling
that we currently use
really struggles with creating ergonomics
that make taking frictionless notes difficult.
We have systems where all the disparate
parts of what we're working on
feel really far away from each other.
We're pushed away from engaging
in conversations with ourselves
as a result of how disparate
all of our tooling feels,
how the process of working with it
is incongruent.
My hope is that we can instead
engage with a process
that makes it really trivial
to write impulsive journaling
about what we're doing.
So much of design is ultimately
just daydreaming.
Good ideas tend to strike us hard,
in a momentary flash of inspiration,
and then they fade just as quickly.
Anyone who's had an idea all at once
in the middle of the night
knows that they're going to have to choose
between either committing to writing it down
or accept that by morning
they'll have lost it.
If we're not writing
what strikes us as important
at the same moment that it's happening,
we're going to lose it.
It's not realistic to expect ourselves
to hold onto our ideas forever
with the same precision
as when we were first inspired.
Okay. I'm gonna call you out real quick.
If I ask all of you "Who wants to read
really excellent documentation?"
I imagine that everyone here
is raising their hand.
We want code to make sense
and we want to know what
the original developer had in mind.
Even the original developer themselves
would want this just for their own sake.
I know that for me, I can even feel
things becoming less fresh
just after a couple months away
from my codebase.
And that was me from a couple months ago.
They're not around anymore.
Now, here's the rough part.
Here's what I'm really gonna call you all out.
"Who wants to write really excellent documentation?"
Now, I don't know what's happening on your end,
but I'm imagining crickets,
silence, tumbleweeds
blowing through to the horizon.
It's a tough ask.
It's not generally all that rewarding.
If you're writing docs from scratch,
a lot of it involves relearning
the intentions behind crusty old code.
For me, it hurts to not spend that same time
implementing bug fixes and new features.
It just doesn't feel like
a great use of my time.
Even if it's strictly for my own codebase
for my own use, it's hard to sit down and do it
even when I know how much I would benefit from it.
My thinking is that when you write rough,
piecewise daydreaming as you go,
it's so much easier to not only
begin writing documentation early in your process,
but also to stay consistent about not slouching into
an accumulation of a backlog.
So not only does writing documentation early
make us more likely to keep that habit going,
but it also makes the documentation
we do write way more robust.
When fiction meets reality
and we start writing out code
that is constrained by the real world
and not just our imagination,
we learn that things we assumed about our design
aren't going to work out in practice.
Because of this, we can enter
into a sort of situation
akin to boiling a frog in a pot of water.
Frogs don't notice that they're being boiled
if the water is only heated gradually enough.
We decide to adjust our design only a little bit
without changing the documentation right away.
Doing that once is fine,
but I don't believe for a second
that we're only going to do it once.
We can find ourselves surprised
that as time goes on,
our code looks nothing like our spec,
and we lose the thread of what our code
was supposed to do in the first place.
When we stake our intentions clearly and early,
you ground yourself in them.
You reduce the risk of straying from them.
You have clear reference
for what you want your code to do,
and you reduce the risk
of having its purpose shift over time.
When we take turns alternating
between writing code and documentation
rather than acting, you know,
as having it all as one step,
we risk taking turns just moving
our goalpost back and forth.
So we've seen how our Bookclub files get us
all sorts of amazing features
and practical benefits.
But we might be starting to notice a pattern
as we continue to engage in conversation
and work with our document
and watch it grow in size.
We originally created our Bookclub file
with the hope to reduce
what we would need to keep track of
and to reduce our level of overwhelm.
We might find that as our Bookclub file grows,
we're encountering more detail
than we can practically parse, manage,
and decipher intention from.
It can be easy to enter into a situation
where we're drowning in the breadth of our notes,
and in doing so we've recreated the same problem
we originally set out to solve.
Writing out every single detail helps us a lot
to make sense of things at first,
but then after a while, we can encounter
a signal-to-noise problem
when we try to make meaning from too many details.
This is where tapas come in.
So tapas in Spanish cuisine are appetizers.
What's notable about tapas
is that you can bring a bunch of them together
to make a full meal.
In the context of Bookclub Tapas,
they serve a similar role.
The idea is that we write flavorful libraries
that together form a full program.
We have a full program,
but it's made from discrete modules.
The idea behind tapas is that instead of creating
one perfect, "solves everything" codebase,
we want to create a whole bunch
of separate libraries
that themselves nail a specific subdomain.
And once these libraries
are all brought together,
they form the whole that we're seeking.
Once our Bookclub file becomes big enough
such that we feel like our scope can be split
into multiple libraries,
that's when we want to take the opportunity
to split our program up into parts, into Tapas.
So, maybe one of the best ways
to understand what makes a good Tapa
is to first examine what does not make a good Tapa.
The single most important thing
to understand about Tapas
is that they themselves are substantial.
There's a lot of back and forth
on the idea of micro-libraries,
their merits, their dangers,
and when and where they kind of work best.
I think the distinction
that I would like to draw
is that I think that tapas belong in the larger end
of scale and complexity for microlibraries
rather than the smaller end.
I think particularly small helpers
like NPM's is-odd
are a good example of something
I think does not constitute a good Tapa.
Meanwhile, I think Python's Requests library
is a really good example of a Tapa.
I believe Requests only does HTTP connections,
but I feel like that's not so simple and straightforward
that you can just go ahead and implement it
on your own real quick.
A real danger of creating
helper libraries that are too small
is that we don't remove abstraction
nearly as much as we postpone it.
If our libraries are small,
but the glue code that binds them is large,
we haven't done anything
to reduce complexity
or employ abstraction in a meaningful way.
If all of the complexity exists in our glue code,
we've simply replaced our functions
with libraries of the same size and purpose.
Our codebase is still monolithic
instead of having meaningfully divided scope.
I think that a good Tapa
ought to feel like augmentations
or extensions to the standard library.
You know, maybe something kind of
akin to Scheme's SRFI system.
I think that the goal of good Tapas
is not to solve a particular problem,
but instead to solve a particular class of problem.
The goal of a well-written Tapa
is to solve needing to do hard work in general
rather than solving what can only really be
an individual need
of an individual program.
I feel like Tapas are most helpful
when we instead seek to solve
a larger overarching problem
that intersects with the problem space of our code base.
When we have a handful of Tapas
that are roughly the same size and scale,
the glue code that marries them
is also roughly the same size and scale.
As a heuristic, I try to aim for any function
being approximately 3 calls in length,
and then any Tapa being between 6
and 12 functions in length.
The number of Tapas themselves
can be as many or as few as you need,
but then your Tapas can split into
their own separate Tapas as needed.
My hope is that the collection of our Tapas,
especially as we create
dependency chains among them,
is that each next Tapa is a trivial case
of the one prerequisite to it.
Every Tapa is a meaningful,
human-readable abstraction
that enables us to feel confident about our tooling
without drowning in detail.
The whole stack can be understood by humans,
but we only have to focus on
any one piece of it at a time,
rather than focusing on the entire stack all at once.
We can practically achieve
a huge final product,
but each individual step
in working towards that goal
is still at a human scale.
One thing I want to make sure to point out,
one thing I want to make sure
to point out explicitly, real quick,
is that having access
to a hygienic macro system,
like the ones that we have in Lisps,
makes for an amazing experience
for creating Tapas.
The types of abstractions that we can do
by modifying syntax at compile time
makes for incredibly intuitive
and ergonomic tooling.
So we've talked quite a bit about
what I think makes a Tapa good,
but I think maybe the best way
to understand the concept
is to have a look at the whole workflow in practice.
I've been working on this, currently
unnamed, Elisp program recently.
It's a validator for the filetags lines
of my Org Mode files.
So I have Org Mode files
under my Documents directory,
organized in this hierarchical way,
and the nested directories have meaningful names.
I want the headers of my Org files to be tagged
in accordance with the sequence
of the names of the directories.
I do this by having the file-tags line
at the top of the file
just list the path segments in order.
If I have an Org file in the directory
"~/Documents/foo/bar",
the file-tags line has the tags "foo" and "bar".
This is totally fine to do by hand,
but I want a program
that recursively searches through my directories
to validate that the tags are correct
because it's easy to drop something.
This scale of problem is actually kind of perfect
for demonstrating how Bookclub Tapas work in action.
We have a problem
that's mostly rather simple,
but it has a lot of moving pieces.
We want to iterate over directories recursively,
we want to do string manipulation,
we want to parse buffers,
and we want to edit buffers.
All of these tasks are simple enough on their own,
but it's deceptively easy
to start tripping over ourselves
when we feel like it's necessary
to do all of these different things in one step.
So there are a ton of great string
manipulation tools for Emacs,
so that's checked off,
that's done, taken care of.
I'm still kind of daydreaming
about writing a wrapper around
some of the Emacs standard libraries
for directory traversal,
just to make it a little bit nicer to work with.
But the big thing
that really struck me as odd
is that there doesn't seem to be a great tooling
for destructuring Emacs buffers
beyond just chaining together
a bunch of editor commands.
Emacs is so buffer-oriented,
I feel like it really deserves a good library
for programmatic buffer destructuring.
I looked around for a bit,
but I couldn't really find anything.
So at the end of the day,
I could definitely just grit my teeth
and put my head down and just use tools
that feel cumbersome to work with if I wanted to.
I could write something
that's "good enough"
just for the purpose of my package
and then hide it deep inside the code base.
I could absolutely do that.
But I can't help but think about how
after I properly write the tooling I'm missing,
I'm really going to be thanking myself
in terms of reduced implementational complexity,
reduced bug hunting, real reusability,
and ultimately really just a deep sense of pride
in knowing that I took the time
to do something in a way that feels "right."
This right here is the perfect time
to split off Tapas.
Any time that we find ourselves
reaching for a fictional dependency,
wishing that someone had written
a library like this...
We can take that opportunity
to remember that we are "someone."
We can write that library ourselves,
and we deserve to write that library
because we deserve to get to use it.
So I'm going to briefly show
a Bookclub buffer
for a program called Squint.
It's the buffer destructure
that I've been talking about, and it's real.
It's a wrapper around
Emacs's narrowing functionality
and regular expression search.
It's not totally done,
and will likely see some breaking changes,
but I really like where it is.
I'll be posting it in its current state
on some of the big source repository sites
relatively soon.
I think it has a good feature,
which is really quite exciting.
And it'll likely probably get split off
into its own Tapas.
We'll see. No matter what,
I do recommend being on the lookout for it,
because I think it'll be
a really excellent demonstration
of some of the solid ideas
behind how to get rolling with Bookclub Tapas.
So I have my background section
where I'm basically just sort of laying out,
you know, what the objective is for the program.
I have my vision where I'm doing
some daydreaming about, you know,
how this all ought to work.
I date stamped this.
As you can see, it's from a while ago,
but I still have the full context of, you know,
all the things that I've done working on this.
I listed out a bunch of ideas
for different forms for functions macros.
I did different pieces of research.
Yeah, I was trying to figure out
for the width restriction macro,
what types does it take?
And I did a whole bunch of tests
to try and ultimately figure it out.
Because it claims in the documentation,
I believe, that it will just take
any type for labels.
But in my testing, that's not
ultimately what I found.
The results of my tests
is that symbols, numbers, they work.
Strings do not.
I'm not sure why that is.
But for my purposes,
this is what I need to know.
I have my development focuses here.
So I have my assorted goals
for different directions
I want to take the program.
And then lastly, I have my functions, my macros.
And this right here
is the titular macro.
This is ultimately the big meat
of the program.
And it's all contained happily organized
inside my Bookclub file.
I'm quite happy with it.
I think it looks really nice.
So what else does Bookclub tapas do?
I don't know. It probably does a lot of stuff.
It does all sorts of stuff
that I don't know about yet,
but this is where you come in.
I'm really excited to see what people do
when they take these ideas
and run with them.
And if you have something really cool you're doing with it,
please email me and come talk to me about it.
I'd love to hear about it.
Again, my email is hello@ElephantErgonomics.com.
So last, before we wrap up,
I want to go ahead and give
a quick plug for my services.
I am an independent software engineer
that has an emphasis in backend design
and general automation.
In particular, I have an emphasis
in that really cool new generative AI thing
that everyone's been talking about recently.
If you have a headache,
you have some sort of pain point
for your small or large business,
you wish you could just wiggle your nose
and have disappear, come talk to me.
I'll make it disappear. I love doing that.
Reach out to me at hello@ElephantErgonomics.com.
If you think that Bookclub Tapas
would be a great fit for your team and your project,
I'd love to hop on and help you
get the ball rolling quickly.
Go ahead and email me at hello@ElephantErgonomics.com.
Lastly, if you're a member
of the larger Lisp community
and you want to fund independent software development
for things that really excite you,
for passion projects
that make our ecosystem richer,
I'd love to look into accepting independent funding
so I can commit more hours
toward making that happen.
Some of the projects that I want to work on
are a Python Foreign Function Interface for Guile Scheme,
a framework for rapidly creating simulation games
that feels just as simple
as writing Emacs configurations,
I want to work on getting
a full graphical web browser inside of Emacs,
and I want to finish programs like Squint.
These are just some of the projects
I want to work on,
but I need funding to do so.
If you want to see these things happen,
send me an email at hello@ElephantErgonomics.com
with both your intention
to pledge a monthly contribution
as well as clarification,
a sort of vote on which project
you would like to see me prioritize.
I would love to have folks reach out
for any of these reasons.
I would just love to talk to you.
Thank you so much for watching!
I really hope that the talk was interesting,
and I'm really excited to see
your thoughts and questions
right now in the Q&A!
Thank you so much for watching. Bye!