brick-house

This commit is contained in:
Motiejus Jakštys 2022-04-24 09:39:56 +03:00
parent 52876c05ad
commit ab3972ff04
4 changed files with 175 additions and 86 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

@ -26,7 +26,7 @@ body {
}
p {
margin: 0.5em 0;
margin: 1em 0;
}
header {
@ -78,14 +78,12 @@ figure {
img,figcaption {
margin: 10px auto;
width: 90%;
max-width: 600px;
padding: 0 10px;
height: auto;
display: block
}
@media (max-width:500px) {
@media (max-width:600px) {
img,figcaption {
margin: 0;
padding: 0;
@ -249,7 +247,7 @@ nav#TableOfContents ul {
margin: 0;
}
@media (max-width:500px) {
@media (max-width:600px) {
nav#TableOfContents {
float: none;
width: 100%;
@ -294,7 +292,7 @@ nav#TableOfContents ul {
content: ' - ';
}
@media (max-width:500px) {
@media (max-width:600px) {
.article-list li .article-date,
.article-date li .article-title {
display: block;

View File

@ -1,11 +1,95 @@
---
title: "git-subtrac and Zig"
title: "zig, git-subtrac and dependencies"
date: 2022-04-23T05:37:51+03:00
draft: true
---
TLDR: I wish plain `git clone <repository>` would check out submodules if they
are in the same repository.
TLDR: modern programming languages make it very easy to add many dependencies.
That is nice for development, but a nightmare for maintenance. Unfortunately,
zig is following suit. I wish we could accept that adding dependencies does not
have to be trivial. If we accept that, thanks to ubiquity of git, we may have
almost solved the dependency problem.
Adding dependencies
-------------------
All of the programming languages I've used professionally whose name does not
start with "c"[^1] have package managers[^2], which make "dependency
management" easy. These package managers will, as part of the project's build
process, download and build the dependencies, making adding and using
third-party dependencies easy.
Because C/C++ still does not have a universal package manager, not adding
external dependencies to C/C++ is the path of least resistance. Instead, it is
common to rely on libraries already installed in the system. Because of this
cultural difference, there is a plethora of dependency managers that will
discover, but not install dependencies: autotools, cmake, pkg-config and
others. As a result, C/C++ projects I've been involved usually had 0-5
non-system dependencies, whereas non-C/C++ projects -- tens, hundreds or
thousands[^3]. Having many system dependencies is painful for user experience,
so (the good) C/C++ projects also avoid having too many of them.
Not doing things that are easy to do requires discipline: brushing teeth,
limiting candy intake, not adding dependencies all over the place. If it is
easy to add dependencies and there is no discipline not doing so, the project
will gain a lot of dependency "weight" with time.
{{<img src="_cheese/2022/brick-house.jpg"
alt="House made out of Duplo pieces"
caption="Just like this brick house, \"modern\" package managers are optimized for building, not maintenance. Photo mine, house by my sons."
hint="photo"
>}}
In Go and Python small number of dependencies is often a sign of care and
quality. [mattn/go-sqlite3](https://github.com/mattn/go-sqlite3),
[uber/zap](https://github.com/uber-go/zap),
[apenwarr/redo](https://github.com/apenwarr/redo) and
[django](https://djangoproject.com) are good examples. Making it easy to depend
on external code is is convenient during development, but frees developers from
their basic right (or obligation?) to audit understand them. And adds real
long-term maintenance costs.
The costs of just having dependencies are huge. I haven't done a survey and
have only my experience to base this on (read: "many anecdotes of me failing to
build stuff I wrote a decade ago"). But it is bad enough that I have a
dependency checklist and am prepared to do grunt work to avoid or strip it.
Here is my checklist:
- Obvious: does it work at all?
- How easy is it to build, run and run it's tests?
- Is it well written? API surface, documentation, tests, error handling, error
signaling, logging, metrics (if applicable), etc.
- It's system dependencies.
- It's transitive dependencies.
If a dependency is well written, but has more transitive dependencies than I
need and there is no good alternative, I will fork it and remove unnecessary
code and dependencies. My recent example is
[sql-migrate](https://github.com/motiejus/sql-migrate).
To sum up, the "modern" languages optimize for initial development experience,
not maintenance. And as [Corbet says][linux-rust]. "We can't understand why
Kids These Days just don't want to live that way". Kids want to build, John,
not maintain. A 4-letter Danish corporation made a fortune by selling toys that
do not need to be maintained: they are designed to be disassembled and built
anew. It is very hard to change the guts of an existing structure without
rebuilding it.
If I may combine Corbet's views with mine: if we understand and audit our
dependencies (and transitive ones), we will have less dependencies and a more
maintainable system. Win-win.
Which brings us to...
Transitive dependencies and git-subtrac
---------------------------------------
[`git-subtrac`][git-subtrac] does not deal with transitive dependencies. At
least not directly. Or I am not aware of it. Ok, I haven't tried.
If we audit and thus understand our dependencies, we will be able to add
transitive ones to our project even without support of git-subtrac. So perhaps
git-subtrac shouldn't care?
I use [`git-subtrac`][git-subtrac] for some of my projects, and am not very
enthusiastic about Zig getting it's own package manager (can we all use
@ -13,8 +97,8 @@ git-subtrac and be done with it?). A few weeks ago in a park in Milan my
conversation with [Andrew Kelley](https://andrewkelley.me/) was something like:
- me: "git-subtrac yadda yadda yadda submodules but better yadda yadda yadda".
- Andrew: "if I clone a repository that uses git-subtrac with no extra
parameters, will it work as expected?"
- Andrew: "if I clone a repository that uses it with no extra parameters, will
it work as expected?"
- me: "no, you have to pass `--recursive`, so git will checkout submodules...
even if they are already fetched."
- Andrew: "then it's a piece-of-shit-approach."
@ -37,80 +121,6 @@ make our dependency unavailable.
It is, howerver, harder to *add* a dependency with submodules than with, say,
`go get <dependency>`. Let's talk about adding dependencies.
Adding dependencies
-------------------
All of the programming languages I've used professionally whose name does not
start with "c"[^1] have package managers[^2], which make "dependency
management" easy. These package managers will download and build the dependency
tree, sometimes conveniently generate a "lock file", so your project has an
illusion of being "reproducible".
C/C++ projects I've been involved usually had 1-5 non-system dependencies,
whereas all others -- tens or hundreds. This uncovers an obvious correlation:
if it's easy to add dependencies, they will be added. En masse. Not adding
dependencies in Go/Python/whatever requires discipline. Slip once, add some
crap -- it will be very hard to remove, as changing dependencies often require
large rewrites. Not adding dependencies in C/C++, however, is the path of least
resistance. However, in the long term, my C/C++ projects tended to survive
longest (or required least amount of changes to build and run after the world
moved on) just because of this.
Making it easy to depend on external code is is convenient during development,
but frees (or denies, depending how one looks at it) developers from their
basic right (or obligation?) to understand them. And adds real long-term
maintenance costs.
To sum up, the "modern" languages optimize for initial development experience,
not maintenance. And as [Corbet says][linux-rust]. "We can't understand why
Kids These Days just don't want to live that way". Kids want to build, John,
not maintain. This house is in desperate need of maintenance, but my son
refuses to do so, and builds a new car instead.
{{<img src="https://dl.jakstys.lt/mtpad/house.jpg"
alt="House of Duplo pieces"
caption="House of Duplo pieces"
width="50%"
>}}
This is why I am always hesitant to pull in code to my project, and have a my
dependency checklist:
- Obvious: does it work at all?
- How easy is it to build, run and run it's tests?
- Is it well written? API surface, documentation, tests, error handling, error
signaling, logging, metrics (if applicable), etc.
- It's system dependencies.
- It's transitive dependencies.
Zooming into the last part: C projects tend to do it well. For Go and Python
projects a small number of dependencies is often a sign of care and quality on
other areas, too. [mattn/go-sqlite3](https://github.com/mattn/go-sqlite3),
[google/brotli](https://github.com/google/brotli),
[apenwarr/redo](https://github.com/apenwarr/redo),
[cmph](http://cmph.sourceforge.net/) are good examples.
If a dependency is well written, but has more transitive dependencies than I
need and there is no good alternative, I will fork it and remove unnecessary
code and dependencies. My recent example is
[sql-migrate](https://github.com/motiejus/sql-migrate).
If I may combine Corbet's views with mine: if we understand and audit our
dependencies (and transitive ones), we will have less dependencies and a more
maintainable system. Win-win.
Which brings us to...
Transitive dependencies and git-subtrac
---------------------------------------
[`git-subtrac`][git-subtrac] does not deal with transitive dependencies. At
least not directly. Or I am not aware of it. Ok, I haven't tried.
If we audit and thus understand our dependencies, we will be able to add
transitive ones to our project even without support of git-subtrac. So perhaps
git-subtrac shouldn't care?
Conclusion
----------
@ -118,10 +128,12 @@ Can git checkout local submodules when they are in the same repository, so our
conversation of reconsidering (or not having) a zig package manager doesn't
stop after 5 seconds?
[^1]: Alphabetically: Erlang, Go, Javascript, PHP, Perl, Python.
[^1]: Alphabetically: Erlang, Go, Java, Javascript, PHP, Perl, Python.
[^2]: Usually written in the same language. Zoo of package managers (sometimes
a couple of popular ones for the same programming language) is a can of worms
in an on itself worth another blog post.
[^3]: `go.sum` of a project I am currently involved clocks around 6k lines.
This is quite a lot for Go, but still peanuts to Node.js.
[git-subtrac]: https://github.com/apenwarr/git-subtrac/
[linux-rust]: https://lwn.net/SubscriberLink/889924/a733d6630e3b5115/

View File

@ -0,0 +1,79 @@
<!--
# Responsive images and resizing
Our picture width is 700 css pixels. "Retina" screens have 2 device pixel ratio
(dpr), crazy folks have 4. So the maximum width of the picture we can possibly
care about is 700*4=2800px.
-->
<!-- image -->
{{ $src := resources.GetMatch (.Get "src") | resources.Fingerprint }}
{{ $max := $src | resources.Fingerprint }}
{{ $j350 := $src.Resize "350x" }}
{{ $j700 := $src.Resize "700x" }}
{{ $j1400 := $src.Resize "1400x" }}
{{ $j2800 := $src.Resize "2800x" }}
{{ with .Get "hint" }}
{{ else }}
{{ errorf "missing value for param 'hint': %s" .Position }}
{{ end }}
{{ $hint := .Get "hint" }}
{{ $w350 := $src.Resize (print "350x webp " $hint ) }}
{{ $w700 := $src.Resize (print "700x webp " $hint ) }}
{{ $w1400 := $src.Resize (print "1400x webp " $hint ) }}
{{ $w2800 := $src.Resize (print "2800x webp " $hint ) }}
<figure {{ with .Get "class" }}class="{{.}}"{{ end }}>
{{ with .Get "link" }}<a href="{{ . }}">{{ end }}
<picture>
<source type="image/webp"
sizes="(max-width: 600px) 350px, 700px"
srcset='
{{- if ge $src.Width "350" }}
{{ with $w350.RelPermalink }}{{.}} 350w{{ end }}
{{- end }}
{{- if ge $src.Width "700" }}
{{ with $w700.RelPermalink }}, {{.}} 700w{{ end }}
{{- end }}
{{- if ge $src.Width "1400" }}
{{ with $w1400.RelPermalink }}, {{.}} 1400w{{ end }}
{{- end }}
{{- if ge $src.Width "2800" }}
{{ with $w2800.RelPermalink }}, {{.}} 2800w{{ end }}
{{- end }}'
/>
<img
sizes="(max-width: 600px) 350px, 700px"
srcset='
{{- if ge $src.Width "350" }}
{{ with $j350.RelPermalink }}{{.}} 350w{{ end }}
{{- end }}
{{- if ge $src.Width "700" }}
{{ with $j700.RelPermalink }}, {{.}} 700w{{ end }}
{{- end }}
{{- if ge $src.Width "1400" }}
{{ with $j1400.RelPermalink }}, {{.}} 1400w{{ end }}
{{- end }}
{{- if ge $src.Width "2800" }}
{{ with $j2800.RelPermalink }}, {{.}} 2800w{{ end }}
{{- end }}'
src="{{ $j700.RelPermalink }}"
{{ if or (.Get "alt") (.Get "caption") }}alt="{{ with .Get "alt" }}{{ . }}{{ else }}{{ .Get "caption" }}{{ end }}"{{ end }}
/>
</picture>
{{ if .Get "link" }}</a>{{ end }}
{{ if or (or (.Get "title") (.Get "caption")) (.Get "attr") }}
<figcaption>{{ if isset .Params "title" }}
<h4>{{ .Get "title" }}</h4>{{ end }}
{{ if or (.Get "caption") (.Get "attr") }}<p>
{{ .Get "caption" }}
{{ with .Get "attrlink" }}<a href="{{ . }}"> {{ end }}
{{ .Get "attr" }}
{{ if .Get "attrlink" }}</a> {{ end }}
</p> {{ end }}
</figcaption>
{{ end }}
</figure>
<!-- image -->