From 316d0d99e9ae463307eac559b4fa28aaefb4390c Mon Sep 17 00:00:00 2001 From: Dominic Ricottone Date: Mon, 19 Sep 2022 15:35:09 -0500 Subject: [PATCH] New post --- .../posts/progress_and_the_lack_thereof.md | 140 ++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 content/posts/progress_and_the_lack_thereof.md diff --git a/content/posts/progress_and_the_lack_thereof.md b/content/posts/progress_and_the_lack_thereof.md new file mode 100644 index 0000000..25795b0 --- /dev/null +++ b/content/posts/progress_and_the_lack_thereof.md @@ -0,0 +1,140 @@ +--- +title: "Progress and the lack thereof" +date: 2022-09-18T17:44:00-05:00 +draft: false +--- + +I revisited some of my oldest coding projects this week. It started as an +effort to clean up old messes that really don't reflect my strengths. But +it was fun and nostalgic, and it also helped me appreciate how much has +changed. + +---- + +Like many others, my oldest Python projects were loose Python files that had to +be called directly. Learning to use *main* functions and write modules with +*entrypoints* always comes later. + +My oldest Python project *with a build process* used `setup.py` and targeted +sdist and bdist formats. Luckily that's a bunch of technical jargon that +has been thrown out the window in recent years. + +`pyproject.toml` has been a long time coming and I welcome it. But I'm afraid +it took too long to get here. All the most searchable documentation for the +file's specification belongs to the *poetry* project, which has plenty of +incompatible extensions to the file. + +I think the continued need for `setup.cfg` hampered interest in migration. But +as I coaxed this project into a `pyproject.toml`-based system, I realized that +*setuptools* finally merged support for PEP 621 earlier this year. +Goodbye, `setup.cfg`! + +I'm happy with where Python packaging landed. It's a shame it took this long. +Or maybe it didn't take *time*, just the [seccession of the packaging +infrastructure team](https://peps.python.org/pep-0609/) from the rest of the +steering council... + +---- + +Another familiar story: The biggest difference between my old and new projects +is type hints. + +I used to typeguard all passed-in arguments as a debugging tool. +(If `int(myint)` fails then clearly `myint` isn't what I want it to be). +This made re-using code a *pain in the ass* at the best of times. + +`mypy` does everything I need without adding brittle code. And it's only gotten +better over the years. Projects stopped using type stubs and started annotating +their own code. + +I think that typing has also encouraged people to implement their own domain- +specific modules instead of becoming dependent on feature-creeped libraries. +(`npm`, anyone?) Type stubs were a hassle and it was much more feasible to fork +a module, kill everything you don't need, and add the three lines of +annotations that `mypy` needed to be happy. + +I think that typing also helped people realize that *if code is hard to +annotate, it probably isn't good code*. Clever programming is and has never +been a good thing. A function does not need to have 11 optional arguments that +fully customize the returned data. *Meta-classes have never been a good idea.* +Sometimes we need a compiler to remind us of that. Unfortunately Python doesn't +have a compiler so we have to make do with static analysis. + +Sadly a large population is convinced that typing is bad, and they've taken +over the show for all intents and purposes. Seeing as the steering keeps +getting +[in](https://peps.python.org/pep-0637/) +[the](https://peps.python.org/pep-0677/) +[way](https://mail.python.org/archives/list/python-dev@python.org/message/VIZEBX5EYMSYIJNDBF6DMUMZOCWHARSO/). + +---- + +I came into the Golang community right around the time that modules became a +thing. Several of my oldest projects were not setup for modules, which +honestly surprised me. I don't remember when I started using them. It must have +been a truly seamless transition. + +That seems all the more likely given how *easy* it was to update my oldest +projects to use modules. + +With that said, I know that I use modules in a slightly unconventional way. +I purposefully do *not* commit `go.sum`. The intention behind that file is to +make build reproducible. Which is a fine goal and all, but it's not *my* goal. +Deleting `go.sum` and beginning every build with `go get -u` ensures that I am +always keeping up-to-date with upstream deprecations and minor version updates. + +---- + +In many ways I consider Go to be the successor of Python. They both have +expressive syntax, and Go's compiler does a great job of type inference. +I've rewritten a few scripts by now and found it a pretty simple process. + +There is one particular hiccup between the two. And it bridges two more things +that have changed about my projects over the years. + +---- + +In some of my earliest Python projects, I struggled with module structure. +I was fascinated by the idea of managing multiple clients, servers, and higher- +level utility libraries in a single repo. With common libraries to de-duplicate +implementations. + +Over time, I learned that this was a bad idea. It's more difficult to make +packaging work correctly. The imports become messy and code keeps moving +between modules, leading to even *messier* imports. + +In Python, flat is always better. + +---- + +When I began using Go, I tried to program in the exact same paradigm. But I +kept finding some difficulty in that pattern, because Go does *not* want a +module to be executable *and* importable. + +This requires some context: In Go, a *main* package is something that can be +compiled and executed. And naturally all projects begin with a main package. +My primary workflow is to make changes to a codebase, insert debug logging +statements, and execute the module on test data. Rinse and repeat until the +changed code passes the visual unit test. But if a module contains a main +package, it *cannot* be used in other modules. So at a late stage of +development I have a hard choice: rewrite the module to use an importable +package, or move the importable code elsewhere. + +My solution is creating a *common* submodule and moving all useful +functionality into that. The root module remains a main package and wraps +the common library. + +I've noticed that the *more common* practice is the reverse of mine: create +a *cmd* submodule that contains one or more main packages. The advantages are +that imports are shorter (i.e. don't need to include `/common` in the package +path and don't need to provide a local name for that import) and that a repo +can host multiple executables. While I concede the former, I don't agree with +the latter. The traditional Unix method is to symlink a core binary to multiple +names, and check the name that was called at runtime to decide which code path +should be followed. + +---- + +This ended up being more of a rant than a reflection, but after sitting on a +draft for a few days, I've decided to publish as-is. + -- 2.45.2