aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/dependabot.yml6
-rw-r--r--FUENTES.md95
-rw-r--r--IDEAS.org5
-rw-r--r--INTERESTING_ARTICLES.org112
-rw-r--r--INTERESTING_PROJECTS.org3
-rw-r--r--README.md6
-rw-r--r--TOOLS.org66
-rw-r--r--blog/content/2006/12/ratas-del-aire.gmi49
-rw-r--r--blog/content/2007/07/everything-is-connected.gmi15
-rw-r--r--blog/content/2008/02/gentlemen-you-can-t-fight-in-here-this-is-the-war-room.gmi25
-rw-r--r--blog/content/2010/03/gatos-azucarados.gmi22
-rw-r--r--blog/content/2011/01/django-o-la-fabrica-de-churros.gmi4
-rw-r--r--blog/content/2011/10/apuntes-sobre-dart.gmi8
-rw-r--r--blog/content/2012/06/desarrollo-web-como-dios-manda.gmi4
-rw-r--r--blog/content/2012/11/que-es-el-rpc.gmi2
-rw-r--r--blog/content/2013/09/recopilatorios-de-grandes-exitos.gmi4
-rw-r--r--blog/content/2024/07/ovejas.gmi2
-rw-r--r--blog/content/2026/03/me-gusta-el-futbol.gmi25
-rw-r--r--blog/content/2026/03/notas.gmi32
-rw-r--r--blog/content/2026/03/peligros-de-bolsillo.gmi45
-rw-r--r--blog/content/2026/03/this-was-supposed-to-be-the-future.gmi27
-rw-r--r--blog/content/2026/04/breve-e-incompleta-historia-del-desarrollo-web.gmi93
-rw-r--r--blog/content/2026/04/el-enemigo-en-casa.gmi35
-rw-r--r--blog/content/gemini.gmi30
-rw-r--r--blog/content/notas/comprar-bajo-en-sodio.gmi176
-rw-r--r--blog/content/notas/index.gmi3
-rw-r--r--blog/content/notas/tecnologia/mama-quiero-ser-programador.gmi (renamed from programming/mama_quiero_ser_programador.md)60
-rw-r--r--blog/content/notes/cliffs/the-tyranny-of-structurelessness.gmi98
-rw-r--r--blog/content/notes/index.gmi19
-rw-r--r--blog/content/notes/interesting-articles.gmi192
-rw-r--r--blog/content/notes/tech/about-apis.gmi (renamed from programming/about_apis.md)15
-rw-r--r--blog/content/notes/tech/about-django.gmi115
-rw-r--r--blog/content/notes/tech/about-relational-databases.gmi (renamed from programming/about_relational_databases.md)17
-rw-r--r--blog/content/notes/tech/containers-might-not-be-the-right-answer.gmi (renamed from programming/containers_might_not_be_the_right_answer.md)48
-rw-r--r--blog/content/notes/tech/crud-is-an-important-unsolved-problem.gmi (renamed from programming/crud_is_an_important_unsolved_problem.md)30
-rw-r--r--blog/content/notes/tech/gadgets/pocket-computers.gmi16
-rw-r--r--blog/content/notes/tech/git-advice.gmi21
-rw-r--r--blog/content/notes/tech/github-annoyances.gmi7
-rw-r--r--blog/content/notes/tech/misc-python-stuff.gmi62
-rw-r--r--blog/content/notes/tech/motivating-example-for-logical-replication-for-dynamic-ui.gmi61
-rw-r--r--blog/content/notes/tech/prolog-vs-sql.gmi (renamed from programming/prolog_vs_sql.md)20
-rw-r--r--blog/content/notes/tech/python-modules-primer.gmi229
-rw-r--r--blog/content/notes/tech/so-you-want-to-play-with-functional-programming.gmi (renamed from programming/so_you_want_to_play_with_functional_programming.md)128
-rw-r--r--blog/content/notes/tech/take-the-less-traveled-road.gmi29
-rw-r--r--blog/content/notes/tech/the-tragedy-of-the-geeks.gmi (renamed from programming/the_tragedy_of_the_geeks.md)32
-rwxr-xr-xblog/post-receive10
-rw-r--r--emacs/emacs.el3
-rw-r--r--misc/comprar-bajo-en-sodio.org178
-rw-r--r--misc/setup.md62
-rw-r--r--misc/take-the-less-traveled-road.md32
-rw-r--r--personal_infra/playbooks/roles/coming_soon_rss/tasks/main.yaml11
-rw-r--r--personal_infra/playbooks/site.yaml12
-rw-r--r--personal_infra/puppet/modules/flexisip/manifests/init.pp8
-rw-r--r--personal_infra/puppet/site/maelcum.mad.int.pdp7.net.pp2
-rw-r--r--personal_infra/puppet/site/nagios.h1.int.pdp7.net.pp2
-rw-r--r--programming/a_plan_against_the_current_web.md19
-rw-r--r--programming/git/combining_repos_with_josh_filter.md252
-rw-r--r--programming/git/git_advice.md22
-rw-r--r--programming/git/github_annoyances.md9
-rw-r--r--programming/java/tutorial.md84
-rw-r--r--programming/on-llms.md123
-rw-r--r--programming/python/about_django.md141
-rw-r--r--programming/python/creating_nice_python_cli_tools.md40
-rw-r--r--programming/python/dependency_handling.md111
-rw-r--r--programming/python/project_setup.md114
-rw-r--r--programming/python/python_modules_primer.md272
-rw-r--r--programming/python/scraping_with_selenium_on_docker.md7
-rw-r--r--programming/the-content-web-manifesto/NOTES.org30
-rw-r--r--programming/the-content-web-manifesto/README.md52
-rw-r--r--programming/we_cant_code_good_so_take_llm_hype_with_a_grain_of_salt.md32
-rw-r--r--programming/when_to_code.md18
-rwxr-xr-xscripts/paperwm18
-rw-r--r--wishlist.org2
73 files changed, 1484 insertions, 2275 deletions
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
deleted file mode 100644
index 0250f915..00000000
--- a/.github/dependabot.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-version: 2
-updates:
- - package-ecosystem: "pip"
- directory: "/blog"
- schedule:
- interval: "weekly"
diff --git a/FUENTES.md b/FUENTES.md
deleted file mode 100644
index 5b6df726..00000000
--- a/FUENTES.md
+++ /dev/null
@@ -1,95 +0,0 @@
-(Véase [el artículo correspondiente en mi blog](https://alex.corcoles.net/2022/11/origenes/).)
-
-También podéis ver [el listado de cuentas que sigo en el Fediverso](https://alex.femto.pub/@yo@alex.femto.pub/following/).
-
-# Gente
-
-* JWZ, uno de los inventores de Internet [https://www.jwz.org/blog/](blog), [https://mastodon.social/@jwz](mastodon).
-* [Maartje Eyskens](https://blahaj.social/@maartje) brevemente excompañera de trabajo, curioso cruce entre retrotecnologia (faxes, Windows 2000, etc.), trenes y tiburones de IKEA.
-* [Danger Mouse](https://www.dangermouse.net/), astrofísico, es mediofamoso por sus webcómics de Lego, decanos de Internet.
- El blog es su vida cotidiana, como profesor particular de ética y ciencia.
- De vez en cuando cuelga artículos divulgativos excelentes.
-* [chechar](https://mastodon.social/@chechar/) ([blog](https://obm.corcoles.net/)), mi nepotismo en forma de hermano.
- Tecnología y esas cosas.
-* Mark Dominus [shitposts](https://shitpost.plover.com) [blog](https://blog.plover.com/) es un friki de las mates, la informática y la lingüística.
-* [Andrew Braybrook](http://uridiumauthor.blogspot.com/) @UridiumAuthor es eso, el autor del videojuego clásico Uridium (y el Paradroid, etc.).
- De vez en cuando descarga artículos brutales sobre programación de videojuegos en todas las eras.
-* [Fabien Sanglard](https://fabiensanglard.net/) es un historiador del videojuego.
-* [apenwarr](https://apenwarr.ca/log/) escribe muy ocasionalmente sobre Internet en general.
-* [David Woodbridge](https://dragonscave.space/@Woody) @dwoodbridge es una personalidad del mundo de la accesibilidad.
-* [Jeff Minter](https://toot.wales/@llamasoft_ox) @llamasoft_ox es un programador de videojuegos clásico que retransmite la vida de su granja de ovejas en Gales.
-* [William Gibson](https://mastodon.social/@GreatDismal) @GreatDismal es el escritor cyberpunk más icónico.
-* [Neil Gaiman](https://mastodon.social/@neilhimself) @neilhimself escritor de fantasía bastante referente.
-
-# Webcómics y humor
-
-* [XKCD](https://xkcd.com/)
-* [El Mundo Today](https://www.elmundotoday.com) aunque ya no es el fenómeno que era, los titulares me hacen reír (aunque nunca entro al artículo).
-* [Penny Arcade](https://www.penny-arcade.com/comic/) una tira clásica sobre subcultura, principalmente videojuegos.
- A veces descarrilan, a veces se pierden en sus mundos de fantasía, pero ocasionalmente aún me hacen sonreír.
-
-# Potenciales Mastodon
-
-
-# Cine
-
-* [The Film Experience](http://thefilmexperience.net/blog/) es un blog de cine al que me aficioné en la prehistoria.
- Lleva activo desde 2009.
- Ha incorporado nuevos columnistas, aunque a mí la verdad sólo me interesan los artículos de su fundador, Nathaniel Rogers.
-* [LoQueYoTeDiga](https://www.elcinedeloqueyotediga.net/) herederos del programa de cine mítico de Cadena Ser.
- Tienen un podcast, pero yo no tengo paciencia para los podcasts.
- Sacan reseñas de todo lo que se estrena en España, el día del estreno.
-
-# Ciencia
-
-* [Quanta Magazine](https://www.quantamagazine.org)
-
-# Tecnología
-
-* [The Verge](https://www.theverge.com/) de los pocos sitios de noticias tecnológicas con calidad editorial.
-* [Arstechnica](https://arstechnica.com) sería el otro, aunque la calidad está bajando últimamente.
-* [Wirecutter](https://thewirecutter.com) es otra cosa.
- Está organizado por categorías (tablets, smartwatches, sillas de oficina, etc.), y proporciona reseñas de sus dos o tres productos favoritos en sus categorías.
- Pese a sus tics, es un excelente punto de partida antes de realizar una compra tecnológica.
-* [Lifehacker](https://lifehacker.com) aunque también ha decaído un poco, de cuando en cuando siguen sacando artículos interesantes.
-* [Liliputing](https://liliputing.com) se especializa en cacharritos, pero un poco menos mainstream.
-* [The Internet Archive](https://blog.archive.org/) es literalmente lo que dicen, hablan de cosas interesantes de conservación digital.
-
-# Videojuegos
-
-* [Polygon](https://www.polygon.com/)
-* [Rock Paper Shotgun](https://www.rockpapershotgun.com)
-
-# Noticias generales
-
-El truco general es usar [Google News](https://news.google.com/).
-Google News agrega muchos medios y aunque su RSS no funciona perfectamente (en mi lector, Miniflux, las entradas se repiten), es mejor que la mayoría de RSS de periódicos y demás.
-Si queréis integrarlo en vuestro RSS, funciona bien para titulares, pero también es adecuado usar su web y aplicación móvil directamente.
-
-A diferencia de la mayoria de webs, encontrar los RSS de Google News no es trivial, así que os pongo los que uso yo:
-
-* [Google News - España](https://news.google.com/rss?hl=es&gl=ES&ceid=ES%3Aes&oc=11)
-* [Google News - Estados Unidos](https://news.google.com/rss?hl=en-US&gl=US&ceid=US%3Aen&oc=11)
-* [Google News - Reino Unido](https://news.google.com/rss?hl=en-GB&gl=GB&ceid=GB%3Aen&oc=11)
-
-Otros sitios serían:
-
-* [The long read](https://www.theguardian.com/news/series/the-long-read) son los artículos "largos" de The Guardian.
- También tienen podcast.
-
-# Compras
-
-* [El blog de las ofertas](https://www.ofertitas.es) tiene avisos de ofertas bastante buenos.
- El volumen es excesivo y tiene bastante paja, pero por ejemplo para campañas de ofertas hacen buenos resúmenes.
-
-# Otros
-
-* [La Página Definitiva](https://www.lapaginadefinitiva.com/) es bastante inclasificable.
- Escriben de todo (principalmente política española y series), pero divertidamente.
-
-# Tuiteros acérrimos
-
-Más que nada pongo esta lista como recordatorio personal.
-Esperemos que algún día se cambien a algo con RSS o abierto.
-
-* @faustianovich un crítico de Fotogramas. Especial porque cada día bien temprano pone todas las películas que echarán en TDT.
diff --git a/IDEAS.org b/IDEAS.org
index f2bdb258..764007e2 100644
--- a/IDEAS.org
+++ b/IDEAS.org
@@ -221,6 +221,11 @@ See https://github.com/alexpdp7/pandocsql https://github.com/alexpdp7/pandoc_dat
- Processes that poll external sources (calendars, org-mode, ticket trackers, etc.) and add elements to the list.
For processes that poll sources without stable ids, the daemon does similarity comparisons for identity
- UIs show the list by ordering, allow reordering, filtering by tag
+
+* Unix domain sockets proxies
+
+- Make it easier to run local daemons (for development, for example) listening on Unix domain sockets (that are named and do not have collisions like port numbers).
+
* SQL2
See https://github.com/EvgSkv/logica, [[https://prql-lang.org/]]
diff --git a/INTERESTING_ARTICLES.org b/INTERESTING_ARTICLES.org
deleted file mode 100644
index ee0fc100..00000000
--- a/INTERESTING_ARTICLES.org
+++ /dev/null
@@ -1,112 +0,0 @@
-* General
-
-- [[https://lukeplant.me.uk/blog/posts/no-one-actually-wants-simplicity/][No one actually wants simplicity]] Simplicity is sacrifice. See also [[https://www.youtube.com/watch?v=SxdOUGdseq4][simple made easy (video)]], [[https://www.seangoedecke.com/wicked-features/][wicked features]].
-- [[https://www.geoffreylitt.com/2025/03/03/the-nightmare-bicycle.html][Avoid the nightmare bicycle]] Good designs expose systematic structure; they lean on their users’ ability to understand this structure and apply it to new situations.
-
-* Programming
-
-- [[https://mikehadlow.blogspot.com/2012/05/configuration-complexity-clock.html][The Configuration Complexity Clock]] Programming languages, configuration files, DSLs for configuration
-- [[https://olano.dev/2023-11-30-code-is-run-more-than-read/][Code is run more than read]] A unified theory of broken software
-- [[https://www.teamten.com/lawrence/writings/java-for-everything.html][Java for Everything]] The advantages of focusing on a single language and how performance and static typing are helpful.
-- [[https://en.wikipedia.org/wiki/Ostrich_algorithm][Ostrich algorithm]]
-- [[https://blog.brownplt.org/2024/04/12/behavior-misconceptions.html][Finding and Fixing Standard Misconceptions About Program Behavior]] About the Standard Model of Languages (SMoL)
-- [[https://dannorth.net/best-simple-system-for-now/][Best Simple System for Now]] A view I disagree on about IAGNI and the opposite concepts, but interesting
-- [[https://mmapped.blog/posts/38-static-types-perfectionism][Static types are for perfectionists]] Our programming style is influenced by our personality and life
-- [[https://www.joelonsoftware.com/2005/05/11/making-wrong-code-look-wrong/][Making wrong code look wrong]] The history about Hungarian notations
-- [[https://www.hillelwayne.com/talks/ese/ddd/][What We Know We Don't Know: Empirical Software Engineering]] 40-minute video about the power of proper sleep, working schedules and stress levels vs. engineering practices
-- [[https://www.hillelwayne.com/post/we-are-not-special/][We are not special]] Second of a series of three articles comparing software engineering with traditional engineering. Mostly dispels some myth and lack of knowledge about traditional engineering.
-
-** Testing
-
-- [[https://testing.googleblog.com/2014/05/testing-on-toilet-risk-driven-testing.html][Testing on the Toilet: Risk-Driven Testing]] "Your tests are a means. The bang is what counts. It’s your job to maximize it."
-- [[https://testing.googleblog.com/2024/10/smurf-beyond-test-pyramid.html][SMURF: Beyond the Test Pyramid]] Test categories and the pyramid are excessively limited models.
-
-** Python
-
-- [[https://lukeplant.me.uk/blog/posts/pythons-disappointing-superpowers/][Python’s "Disappointing" Superpowers]] A convincing defense of dynamic typing
-
-** Rust
-
-- [[https://www.hezmatt.org/~mpalmer/blog/2024/05/01/the-mediocre-programmers-guide-to-rust.html][The Mediocre Programmer's Guide to Rust]]
-- [[https://qouteall.fun/qouteall-blog/2025/How to Avoid Fighting Rust Borrow Checker][How to Avoid Fighting Rust Borrow Checker]]
-
-** Optimization
-
-- [[https://docs.oracle.com/cd/E11882_01/server.112/e41573/technique.htm][The Oracle Performance Improvement Method]] My favorite text about performance tuning- the good advice is not Oracle-specific. Includes a bit more real-world advice than [[https://users.ece.utexas.edu/~adnan/pike.html][Rob Pike's 5 Rules of Programming]].
-- [[https://infrequently.org/series/performance-inequality/][The Performance Inequality Gap, 2024]], [[https://danluu.com/slow-device/][How web bloat impacts users with slow devices]], about janky browser applications and websites.
-
-** Git
-- [[https://blog.gitbutler.com/git-tips-3-really-large-repositories/][Git Tips 3: Really Large Repositories]]
-
-** Accessibility
-
-- [[https://xogium.me/the-text-mode-lie-why-modern-tuis-are-a-nightmare-for-accessibility][The text mode lie: why modern TUIs are a nightmare for accessibility]]
-
-* Systems
-
-- [[https://chrisdown.name/2018/01/02/in-defence-of-swap.html][In defence of swap: common misconceptions]]
-
-* Organizations
-
-- [[https://charity.wtf/2024/07/24/pragmatism-neutrality-and-leadership/][Pragmatism, Neutrality and Leadership]]; the parts about "As a leader, your job is to succeed", "Companies with shitty cultures win all the time".
- This article connects with [[https://hbr.org/2007/03/why-i-wrote-the-no-asshole-rule][The no asshole rule]] book.
-- [[https://charity.wtf/2017/05/11/the-engineer-manager-pendulum/][The Engineer/Manager Pendulum]] Why people should multiclass engineering and management
-- [[https://varoa.net/2024/01/09/how-organisations-cripple-engineering-teams-with-good-intentions.html][How organisations cripple engineering teams with good intentions]] Arguments for having coders code
-- [[https://stackoverflow.blog/2024/06/10/generative-ai-is-not-going-to-build-your-engineering-team-for-you/][Generative AI Is Not Going To Build Your Engineering Team For You]] Bad title; it's about the need for junior coders
-- [[https://luminousmen.com/post/senior-engineer-fatigue][Senior Engineer Fatigue]]
-- [[https://www.joelonsoftware.com/2000/04/06/things-you-should-never-do-part-i/][Things You Should Never Do, Part I]] About rewriting software from scratch
-- [[https://dl.acm.org/doi/10.1145/1464122.1464146][Some observations concerning large programming efforts]] Someone figured most of it out in 1964.
-
-** Project management
-
-- [[https://apenwarr.ca/log/20171213][An epic treatise on scheduling, bug tracking, and triage]] - no non-sense opinions on project management I mostly agree with
-
-* News
-
-- [[https://www.currentaffairs.org/2020/08/the-truth-is-paywalled-but-the-lies-are-free/][The Truth Is Paywalled But The Lies Are Free]] - Excellent title, but the article is so-so
-
-* Society
-
-- [[https://locadeldesvan.com/2025/01/09/contra-la-tecnocratizacion-de-la-vida/][Contra la tecnocratización de la vida]] About the pressure of the modern age and the privilege of being mediocre
-- [[https://www.experimental-history.com/p/face-it-youre-a-crazy-person][Face it: you're a crazy person]] Choosing a job because you like the worst parts of it
-
-* Epistemology?
-
-- [[https://hermiene.net/essays-trans/relativity_of_wrong.html][The Relativity of Wrong]] by Isaac Asimov; all physics theories are strictly "false", but they are very true.
-
-* Meta
-
-- [[https://www.benkuhn.net/progessays/][Essays on programming I think about a lot]]
-- [[https://www.piglei.com/articles/en-programmer-reading-list-part-one/][A Programmer's Reading List: 100 Articles I Enjoyed (1-50)]]
-
-* Infrequent but useful terms
-
-- [[https://en.wikipedia.org/wiki/Abilene_paradox][The Abilene paradox]] is a collective fallacy, in which a group of people collectively decide on a course of action that is counter to the preferences of most or all individuals in the group, while each individual believes it to be aligned with the preferences of most of the others.
-- [[https://en.wikipedia.org/wiki/Dunning%E2%80%93Kruger_effect][The Dunning–Kruger effect]] is a cognitive bias in which people with limited competence in a particular domain overestimate their abilities. Some researchers also include the opposite effect for high performers: their tendency to underestimate their skills. In popular culture, the Dunning–Kruger effect is often misunderstood as a claim about general overconfidence of people with low intelligence instead of specific overconfidence of people unskilled at a particular task.
- [[https://www.frontiersin.org/journals/psychology/articles/10.3389/fpsyg.2022.840180/full][This effect might only be caused by subjects in the bottom quartile can only make optimistic errors placing themselves into a higher quartile, while subjects in the top quartile can only make pessimistic errors placing themselves in a lower quartile]].
-- [[https://en.wikipedia.org/wiki/Gell-Mann_amnesia_effect][The Gell-Mann amnesia effect]] is a cognitive bias describing the tendency of individuals to critically assess media reports in a domain they are knowledgeable about, yet continue to trust reporting in other areas despite recognizing similar potential inaccuracies.
-- [[https://en.wikipedia.org/wiki/Goodhart%27s_law][Goodhart's law]] is an adage that has been stated as, "When a measure becomes a target, it ceases to be a good measure".
- [[https://en.wikipedia.org/wiki/McNamara_fallacy][The McNamara fallacy]] (also known as the quantitative fallacy) involves making a decision based solely on quantitative observations (or metrics) and ignoring all others.
-- [[https://en.wikipedia.org/wiki/Hanlon%27s_razor][Hanlon's razor]] is an adage, or rule of thumb, that states: Never attribute to malice that which is adequately explained by stupidity.
-- [[https://en.wikipedia.org/wiki/Hawthorne_effect][The Hawthorne effect]] is a type of human behavior reactivity in which individuals modify an aspect of their behavior in response to their awareness of being observed.
-- [[https://softwareengineering.stackexchange.com/questions/123627/what-are-the-london-and-chicago-schools-of-tdd][What are the London and Chicago schools of TDD?]] (Just to remember these useful terms)
-- [[https://en.wikipedia.org/wiki/Sturgeon%27s_law][Sturgeon's law]] is an adage stating "ninety percent of everything is crap".
-- [[https://en.wikipedia.org/wiki/Schedule_chicken][Schedule chicken]] is when two or more parties working towards a common goal all claim to be holding to their original schedules for delivering their part of the work, even after they know those schedules are impossible to meet. Each party hopes the other will be the first to have their failure exposed.
-- [[https://everything2.com/title/Your+radical+ideas+about+society%252C+individualism%252C+and+religion+have+already+occurred+to+others][Your radical ideas about society, individualism, and religion have already occurred to others]].
-- [[https://en.wikipedia.org/wiki/Slate_Star_Codex#Lizardman's_Constant][Lizardman's constant]] the approximate percentage of responses to a poll, survey, or quiz that are not sincere
-
-See also [[misc/greek-task-list.md][Greek task list]].
-
-Sources:
-
-- [[https://en.wikipedia.org/wiki/List_of_paradoxes][List of paradoxes]]
-- [[https://en.wikipedia.org/wiki/Unintended_consequences][Unintended consequences]]
-
-* Lost and not found
-
-Some articles I'd like to find here, but haven't been able to find again:
-
-- Enqueuing function calls vs. extending your domain model.
- This article discussed using traditional queues for handling some actions in your application vs. doing this "declaratively".
- For example, enqueue "send notification about x to user y" vs. "add column 'needs_x_notification to users table".
- If I remember correctly, the article contained some insightful arguments for the latter approach I had not thought of.
diff --git a/INTERESTING_PROJECTS.org b/INTERESTING_PROJECTS.org
index 109a4f0a..028ccea4 100644
--- a/INTERESTING_PROJECTS.org
+++ b/INTERESTING_PROJECTS.org
@@ -2,8 +2,6 @@ I have decided to not use GitHub stars any more.
Mainly because it excludes projects not on GitHub.
Also keeping things in a Org mode means I can classify and add notes.
-See also [[TOOLS.org][tools I use]].
-
* Coding
** Version control and code management
- https://github.com/josh-project/josh Simpler alternative to moe/copybara. [[https://github.com/rust-lang/josh-sync][Wrapper tool for CI]]
@@ -248,6 +246,7 @@ See also [[TOOLS.org][tools I use]].
- https://github.com/steveseguin/vdo.ninja Swiss army knife for WebRTC
- https://github.com/Manawyrm/AnotterKiosk Firefox kiosk
- https://linuxae.org.es/ Distribución GNU/Linux para el uso de la Administración Electrónica
+ - https://github.com/plp13/qman Modern man reader
** Email
- https://github.com/simonrob/email-oauth2-proxy
- https://pimalaya.org/ Rust email tools, including sync, MIME...
diff --git a/README.md b/README.md
index fa486d0d..3cc697a1 100644
--- a/README.md
+++ b/README.md
@@ -4,13 +4,10 @@
Como nunca tengo oportunidad de trabajar en modo monorepo, hay que aprovechar esta.
-* [Artículos que cito a menudo](INTERESTING_ARTICLES.org), en una especie de esfuerzo de recopilar cosas que me veo explicando una y otra vez, para en vez de eso dar un artículo bien escrito por alguien más calificado.
* [Proyectos interesantes](INTERESTING_PROJECTS.org).
Antes usaba las estrellitas de GitHub, pero de vez en cuando me encontraba algo (¡cielos!) que no estaba en GitHub (y además, las estrellitas me dan un poco de tirria).
* [Gran parte del código que soporta mi infraestructura personal](personal_infra/).
* [Ideas en las que me gustaría trabajar algún día, pero que cuelgo aquí por si algún incauto pica](IDEAS.org).
-* [Herramientas que uso](TOOLS.org).
-* Artículos varios un poco tipo wiki sobre diversos temas, como [programación](programming/).
* [Mi configuración de Emacs](emacs/).
* [Unos cuantos scripts que uso en mis "estaciones de trabajo"](scripts/) y [un par de barbaridades para montar un contenedor para Distrobox/Toolbox con algunas de las herramientas que uso](workstation/).
* [La definición del modelo de datos del sistema que uso para registrar métricas de salud](weight/), que se convierte en una web gracias a [zqxjkcrud](https://github.com/alexpdp7/zqxjkcrud/), mi framework para desarrollo rápido de CRUD.
@@ -24,13 +21,10 @@ Más cosas escritas [en español](https://alex.corcoles.net/notas/) y [en inglé
(This list is shorter because some material is Spanish-only.)
-* [Frequently-cited articles](INTERESTING_ARTICLES.org), to spare people from my explanations and give them a proper source.
* [Interesting projects](INTERESTING_PROJECTS.org).
Previously I used GitHub stars, but some stuff is not in GitHub (gasp!) (also, I dislike GitHub stars).
* [Most of the code for my personal infra](personal_infra/).
* [Ideas I'd like to work on someday, but mostly making them public to bait people into implementing them instead of me](IDEAS.org).
-* [Tools I use](TOOLS.org).
-* Wiki-like articles on topics such as [programming](programming/).
* [My emacs config](emacs/).
* [Scripts I use on my workstations](scripts/) and [some bizarre stuff to build Distrobox/Toolbox containers with some tools I use](workstation/).
* [The database schema for my health metrics tracking system](weight/), which has a web UI via [zqxjkcrud](https://github.com/alexpdp7/zqxjkcrud/), my CRUD rapid development framework.
diff --git a/TOOLS.org b/TOOLS.org
deleted file mode 100644
index be3e7a1b..00000000
--- a/TOOLS.org
+++ /dev/null
@@ -1,66 +0,0 @@
-See also:
-
-- [[personal_infra][personal_infra]]
-
-* Operating system
-
-- Workstations are mostly Debian
-- Servers are mostly RHEL clones and Proxmox.
-- Thinking about IncusOS
-
-** [[https://github.com/DavHau/nix-portable][nix-portable]] for running some software
-** [[https://linuxcontainers.org/incus/][Incus]] for running VMs and system containers on workstations
-** Podman for application containers
-
-* Desktop
-
-- Gnome with [[https://extensions.gnome.org/extension/6099/paperwm/][PaperWM]] and [[https://extensions.gnome.org/extension/6784/wiggle/][wiggle]] to shake your pointer to embiggen
-
-** Browser: Firefox with...
-*** NoScript
-
-- Accidentally blocks many ads and paywalls.
-- Makes some sites go faster.
-- Blank pages are a good website quality heuristic.
-
-*** Violentmonkey
-
-- Mostly for [[https://github.com/alexpdp7/aelevenymonkey][alevenymonkey]], my own attempt at adding transcriptions to websites
-
-*** Multi-Account Containers (mostly for Enafore)
-
-** Thunderbird for email and calendars (also on Android)
-** WezTerm as a terminal
-
-- Keyboard-driven copy and paste
-
-** Emacs as editor (see [[emacs][emacs]])
-*** org-mode for organization (Orgzly revived on Android, via Nextcloud)
-*** eglot for LSP use
-*** TRAMP for remote editing
-
-** https://www.visidata.org/ for tabular data
-* Services
-** Bitwarden for secrets
-** Migadu for email
-*** [[https://github.com/lefcha/imapfilter][imapfilter]] for forwarding (!)
-** Instant messaging and chat
-
-- IRC with Soju as a bouncer, Senpai as a client (I have given up on mobile chat).
-- DeltaChat as aspirational instant messaging
-- Matrix, Signal, WhatsApp, Telegram, Discord because I have to :(
-- XMPP with Conversations as an experiment
-
-** Miniflux as RSS reader
-** Google Calendar
-** TVmaze for TV show tracking
-** Nextcloud for file storage and sharing
-** Fediverse
-*** Enafore for desktop
-*** Tusky for Android
-*** Incarnator as my own server
-** [[https://ledger-cli.org/][Ledger]] for accounting
-** Google services
-*** Maps
-*** Spreadsheets
-*** OpenFoodFacts
diff --git a/blog/content/2006/12/ratas-del-aire.gmi b/blog/content/2006/12/ratas-del-aire.gmi
index 0caa035b..3a981247 100644
--- a/blog/content/2006/12/ratas-del-aire.gmi
+++ b/blog/content/2006/12/ratas-del-aire.gmi
@@ -1,50 +1,13 @@
# 2006-12-25 Ratas del aire
-Sólo hay una manera en la que un británico de pura cepa puede responder al titular de "se contrata francotirados para eliminar las palomas de Kingston". Llenar la página de la noticia [1] de comentarios surrealistas. Algunos extractos:
+=> https://www.surreycomet.co.uk/news/1039169.marksman_called_in_to_kill_kingstons_pigeons/ Sólo hay una manera en la que un británico de pura cepa puede responder al titular de "se contrata francotirados para eliminar las palomas de Kingston". Llenar la página de la noticia de comentarios surrealistas. Algunos extractos:
-=> http://www.surreycomet.co.uk/pigeoncomments/display.var.1039169.0.marksman_called_in_to_kill_kingstons_pigeons.php [1] la página de la noticia
+> What a lot of fuss over nothing. Everyone knows pigeons can't be killed, they are immortal and immune to bullets. Where I come from we worship the pigeon deity and never look them in the eye as this can turn a man to stone.
->
->
->
-> What a lot of fuss over nothing. Everyone knows pigeons can't be killed,
-> they are immortal and immune to bullets. Where I come from we worship the
-> pigeon deity and never look them in the eye as this can turn a man to
-> stone.
->
->
+> I'm horrified at the very idea anyone might want to harm these gentle creatures. I myself was raised by pigeons after being abandoned in Trafalgar Square as a young nipper. Therefore I know how noble and generous a species they really are. If anyone were to kill a pigeon in this way, it would be as though they are slaughtering one of my own family. It's murder, I say!
->
->
->
-> I'm horrified at the very idea anyone might want to harm these gentle
-> creatures. I myself was raised by pigeons after being abandoned in
-> Trafalgar Square as a young nipper. Therefore I know how noble and
-> generous a species they really are. If anyone were to kill a pigeon in
-> this way, it would be as though they are slaughtering one of my own
-> family. It's murder, I say!
->
->
->
> [...]
->
->
->
-> I know what you mean, reader. I was raised by yaks but I'm sure the
-> experience was similar. How about a council worker cull instead.
->
->
->
->
->
-> My elder sister was held captive for nine days by a flock of rock pigeons
-> on a small island near Malta in 1979 - it may have been Gozo but I'm not
-> too sure. (Sorry about that.) As you might gather she suffers from
-> nightmares and flashbacks but she has also developed a loathing of millet
-> seeds for some strange reason. She is in full support of the cull and, in
-> actual fact, she has already applied for the job and fully intends to
-> carry out her duties as soon as possible - whether she gets the job or
-> not. Be careful around town folks - she's not a good shot.
->
->
+> I know what you mean, reader. I was raised by yaks but I'm sure the experience was similar. How about a council worker cull instead.
+
+> My elder sister was held captive for nine days by a flock of rock pigeons on a small island near Malta in 1979 - it may have been Gozo but I'm not too sure. (Sorry about that.) As you might gather she suffers from nightmares and flashbacks but she has also developed a loathing of millet seeds for some strange reason. She is in full support of the cull and, in actual fact, she has already applied for the job and fully intends to carry out her duties as soon as possible - whether she gets the job or not. Be careful around town folks - she's not a good shot.
diff --git a/blog/content/2007/07/everything-is-connected.gmi b/blog/content/2007/07/everything-is-connected.gmi
index f4552c08..fe576d52 100644
--- a/blog/content/2007/07/everything-is-connected.gmi
+++ b/blog/content/2007/07/everything-is-connected.gmi
@@ -1,18 +1,9 @@
# 2007-07-09 Everything is connected
-De los The B-52's [1], a Blondie [2] a...
+=> https://www.youtube.com/watch?v=rSEMjZ6YqGI De los The B-52's...
-=> https://youtube.com/watch?v=Y4dUS7v2NIQ [1] The B-52's
-=> https://youtube.com/watch?v=qW6OrdLkCLU [2] Blondie
+=> https://www.youtube.com/watch?v=O_WLw_0DFQQ ... a Blondie a...
->
->
->
-> Hi, it's Deb. You know, when I woke up this morning I had a realization
-> about myself. I was always Blondie. People always called me Blondie, ever
-> since I was a little kid. What I realized ia that at some point I became
-> Dirty Harry. I couldn't be Blondie anymore, so I became Dirty Harry.
->
->
+> Hi, it's Deb. You know, when I woke up this morning I had a realization about myself. I was always Blondie. People always called me Blondie, ever since I was a little kid. What I realized ia that at some point I became Dirty Harry. I couldn't be Blondie anymore, so I became Dirty Harry.
...¡el tito Clint!
diff --git a/blog/content/2008/02/gentlemen-you-can-t-fight-in-here-this-is-the-war-room.gmi b/blog/content/2008/02/gentlemen-you-can-t-fight-in-here-this-is-the-war-room.gmi
index b838390d..fc8021c0 100644
--- a/blog/content/2008/02/gentlemen-you-can-t-fight-in-here-this-is-the-war-room.gmi
+++ b/blog/content/2008/02/gentlemen-you-can-t-fight-in-here-this-is-the-war-room.gmi
@@ -1,26 +1,11 @@
# 2008-02-29 Gentlemen, you can't fight in here! This is the War Room
->
->
->
-> According to our sources, the Home lobby for the Incognito dogfighter will
-> serve as a "war room" where up to eight players can meet to discuss their
-> strategy for an upcoming match. Sure, you could do that with text or voice
-> chat, but it sounds like the developers have gone the extra mile to make
-> planning your battles even cooler.
->
->
->
-> Using a three-dimensional "sand table" replication of the level, Warhawk
-> players are said to be able to lay out their strategies in a properly
-> scaled space. They'll even be able to place little army men avatars around
-> the map to envision their formations, something we hope they can also do
-> with teeny tiny Warhawk planes, tanks and Jeeps.
->
->
+> According to our sources, the Home lobby for the Incognito dogfighter will serve as a "war room" where up to eight players can meet to discuss their strategy for an upcoming match. Sure, you could do that with text or voice chat, but it sounds like the developers have gone the extra mile to make planning your battles even cooler.
-Es sólo un rumor [1], no pienso comprarme una PS3 y no creo que juegue al Warhawk.
+> Using a three-dimensional "sand table" replication of the level, Warhawk players are said to be able to lay out their strategies in a properly scaled space. They'll even be able to place little army men avatars around the map to envision their formations, something we hope they can also do with teeny tiny Warhawk planes, tanks and Jeeps.
-=> http://kotaku.com/361562/how-warhawk-makes-home-really-really-cool [1] un rumor
+Es sólo un rumor, no pienso comprarme una PS3 y no creo que juegue al Warhawk.
Pero mola. Mucho.
+
+=> https://web.archive.org/web/20090422074114/http://kotaku.com/361562/how-warhawk-makes-home-really-really-cool Kotaku - How Warhawk Makes Home Really, Really Cool
diff --git a/blog/content/2010/03/gatos-azucarados.gmi b/blog/content/2010/03/gatos-azucarados.gmi
index 46076866..e98e6f6e 100644
--- a/blog/content/2010/03/gatos-azucarados.gmi
+++ b/blog/content/2010/03/gatos-azucarados.gmi
@@ -1,23 +1,5 @@
# 2010-03-16 Gatos azucarados
->
->
->
-> 8 Professors are in the 9th year of a 12 year encyclopedia writing
-> project. The youngest is Bertram Potts who specializes in language and
-> grammar. When Professor Potts discovers that his section on slang is
-> outdated, he sets out to research the topic. Nightclub performer Sugarpuss
-> O'Shea is engaged to gangster Joe Lilac. When she discovers that the
-> police are after her, she must find a place to lay low. Then Potts meets
-> Sugarpuss. He is impressed with her slang and wants to study it further.
-> Sugarpuss uses his study as an excuse to invite herself to stay with the 8
-> Professors at their residence--a perfect hideout. She wins over the older
-> Professors teaching them how to do a Conga Line and earns a marriage
-> proposal from 'Pottsie' by showing him 'yum-yum'. However, Joe Lilac
-> resurfaces with other plans for Sugarpuss.
->
->
+> 8 Professors are in the 9th year of a 12 year encyclopedia writing project. The youngest is Bertram Potts who specializes in language and grammar. When Professor Potts discovers that his section on slang is outdated, he sets out to research the topic. Nightclub performer Sugarpuss O'Shea is engaged to gangster Joe Lilac. When she discovers that the police are after her, she must find a place to lay low. Then Potts meets Sugarpuss. He is impressed with her slang and wants to study it further. Sugarpuss uses his study as an excuse to invite herself to stay with the 8 Professors at their residence--a perfect hideout. She wins over the older Professors teaching them how to do a Conga Line and earns a marriage proposal from 'Pottsie' by showing him 'yum-yum'. However, Joe Lilac resurfaces with other plans for Sugarpuss.
-Otra peli [1] por ver...
-
-=> https://www.imdb.com/title/tt0033373/ [1] Otra peli
+=> https://www.imdb.com/title/tt0033373/ Otra peli por ver...
diff --git a/blog/content/2011/01/django-o-la-fabrica-de-churros.gmi b/blog/content/2011/01/django-o-la-fabrica-de-churros.gmi
index 904dd1a4..0935fbe9 100644
--- a/blog/content/2011/01/django-o-la-fabrica-de-churros.gmi
+++ b/blog/content/2011/01/django-o-la-fabrica-de-churros.gmi
@@ -7,7 +7,7 @@ Estos días me he encontrado frente a una web sencilla, pero que a mi juicio no
¿A parte de esto, qué otras virtudes tiene Django?
* Vistas genéricas. En particular, lista/detalle sobre los modelos de datos, resolviendo correctamente paginación, ordenación, filtrado, etc. Tiene también vistas y maquinaria para hacer CRUD, que supongo funcionan bien pero que no he usado
-* Usa HTML/HTTP "correcto" sin hacer cosas raras, añadir Javascripts innecesarios, serializaciones raras, etc. Todo muy limpio
+* Usa HTML/HTTP "correcto" sin hacer cosas raras, añadir JavaScripts innecesarios, serializaciones raras, etc. Todo muy limpio
* Está documentado. No llega al nivel de Java o Spring, pero desde luego, comparado con Rails y otras estrellas de código libre...
* No usa generación de código. Odiamos la generación de código.
@@ -18,7 +18,7 @@ Pero también le encuentro algún que otro defecto:
* El sistema de plantillas está muy bien, pero es "regular" y no "gramatical", con lo que no admite expresiones donde debería ni otras estructuras muy convenientes. JSP con fragmentos de tag es *muy* superior
* Tengo la sospecha que el funcionamiento sobre JVM no será para tirar cohetes. Además, si nos interesa funcionar sobre JVM, nos tenemos que limitar a Django 1.1 y evitar 1.2 de momento.
* En general el sistema de internacionalización está muy bien, pero no soporta internacionalización en el modelo de datos (i.e. campos multilingües en las entidades)
-* No viene con nada para hacer Javascript/AJAX, aunque seguramente no sería de mi agrado, claro
+* No viene con nada para hacer JavaScript/AJAX, aunque seguramente no sería de mi agrado, claro
A pesar de esto, creo que es el mejor "framework completo" que he visto. Como plataforma "básica", sigo prefiriendo Java + Spring + Servlets + JSP + JSTL, pero creo que Django puede tener un lugar bastante importante en el arsenal de un desarrollador web. La pregunta es, ¿cuál es ese lugar?
diff --git a/blog/content/2011/10/apuntes-sobre-dart.gmi b/blog/content/2011/10/apuntes-sobre-dart.gmi
index 2fb60e7e..432378e7 100644
--- a/blog/content/2011/10/apuntes-sobre-dart.gmi
+++ b/blog/content/2011/10/apuntes-sobre-dart.gmi
@@ -2,9 +2,9 @@
Google ha sacado hoy Dart[1].
-El apunte rápido (que seguro que otros mejoran) es que es un verdadero **Java**script. Es un lenguaje muy muy Java que compila a Javascript. Las diferencias con Java van por dos lados:
+El apunte rápido (que seguro que otros mejoran) es que es un verdadero **Java**Script. Es un lenguaje muy muy Java que compila a JavaScript. Las diferencias con Java van por dos lados:
-* Adecuaciones para funcionar bien cuando se compila a Javascript- i.e. no hay threads, hay "isolates", etc.
+* Adecuaciones para funcionar bien cuando se compila a JavaScript- i.e. no hay threads, hay "isolates", etc.
* Esas mejoras puntuales de Java que llevamos pidiendo a gritos desde hace siglos
Las mejoras de Java son de ovación cerrada:
@@ -18,11 +18,11 @@ Las mejoras de Java son de ovación cerrada:
Siendo realistas, cubre la mayoría de "defectos" "resolubles" de Java. No, no tiene inferencia de tipos, ni lambdas con excepciones chulas, ni "final" por defecto... y quizás no es todo como uno lo había soñado, pero es una solución práctica y disponible **hoy**.
-Eso es lo positivo. En lo negativo, el tipado opcional me escama- y me duele que signifique sacrificios (hay ahí una cosilla un poco rara con las funciones que no devuelven valor que me deja intranquilo). Me queda la curiosidad de estudiar los isolates para saber si aportan algo o si son sencillamente la manera correcta de montar concurrencia en código que será compilado a Javascript y ejecutado por los motores de Javascript existentes.
+Eso es lo positivo. En lo negativo, el tipado opcional me escama- y me duele que signifique sacrificios (hay ahí una cosilla un poco rara con las funciones que no devuelven valor que me deja intranquilo). Me queda la curiosidad de estudiar los isolates para saber si aportan algo o si son sencillamente la manera correcta de montar concurrencia en código que será compilado a JavaScript y ejecutado por los motores de JavaScript existentes.
He visto otras cosas que aún no me he mirado a fondo que no sé dónde colocar: soporte en el lenguaje para factorías, "const" el sistema de librerías y que null sea un objeto; es difícil saber si serán cosas buenas o malas.
-En fin, cosas interesantes. No parece, sin embargo, que Dart aspire de momento a ser algo más que un sustituto de Javascript (algo que no me interesa mucho- el principal problema de Javascript no es el lenguaje en sí, en mi opinión)... con lo que **para mi**, no es muy interesante de momento.  Si algún día se planta como una alternativa para desarrollo de aplicaciones y para programación, tiene  la oportunidad de ser Java++, pero sin añadir la complejidad y cerradez de C#... pero ni siquiera sé si Google pretende que lo sea (ese rol lo quieren para... ¿Go? ¿Dart? ¿Java? ¿Python?) .
+En fin, cosas interesantes. No parece, sin embargo, que Dart aspire de momento a ser algo más que un sustituto de JavaScript (algo que no me interesa mucho- el principal problema de JavaScript no es el lenguaje en sí, en mi opinión)... con lo que **para mi**, no es muy interesante de momento.  Si algún día se planta como una alternativa para desarrollo de aplicaciones y para programación, tiene  la oportunidad de ser Java++, pero sin añadir la complejidad y cerradez de C#... pero ni siquiera sé si Google pretende que lo sea (ese rol lo quieren para... ¿Go? ¿Dart? ¿Java? ¿Python?) .
=> http://www.dartlang.org/ 1: http://www.dartlang.org/
diff --git a/blog/content/2012/06/desarrollo-web-como-dios-manda.gmi b/blog/content/2012/06/desarrollo-web-como-dios-manda.gmi
index e100c7c6..3b1ea8a9 100644
--- a/blog/content/2012/06/desarrollo-web-como-dios-manda.gmi
+++ b/blog/content/2012/06/desarrollo-web-como-dios-manda.gmi
@@ -14,13 +14,13 @@ Una manera fácil de comenzar es por el lenguaje. Es conveniente que escojamos u
Comenzando por el principio, un buen punto de partida es mi querido TIOBE[1]. El TIOBE es un ránking de la popularidad de los lenguajes de programación calculado a partir de su presencia en la web. La metodología es inevitablemente discutible, pero el ránking está bastante alineado con mi percepción, así que para mi, es una opción cómoda.
-En el top 20 (a junio de 2012) encontramos tan solo 8 lenguajes utilizados comunmente para el desarrollo web: Java, C#, PHP, Python, Perl, Ruby, Javascript y Visual Basic .NET. Fuera del Top 20 encontramos muy poquitas opciones (Haskell, Scala y poco más), así que nos ceñiremos a estos.
+En el top 20 (a junio de 2012) encontramos tan solo 8 lenguajes utilizados comunmente para el desarrollo web: Java, C#, PHP, Python, Perl, Ruby, JavaScript y Visual Basic .NET. Fuera del Top 20 encontramos muy poquitas opciones (Haskell, Scala y poco más), así que nos ceñiremos a estos.
Vamos a descartar unos pocos:
* PHP[2]: pese a ser un lenguaje explícitamente diseñado para el desarrollo web, en mi opinión PHP nunca debe usarse para desarrollar un proyecto desde 0- a no ser que lo que queramos desarrollar sea extremadamente mínimo- ya sea porque se trate de un desarrollo extremadamente pequeño o bien que pretendamos reutilizar completamente un desarrollo existente como Wordpress o Magento. Desarrollar grandes bases de código en PHP es un ejercicio frustrante ya que, sencillamente, no está pensado para ello. Sus limitaciones en cuanto a modelo de ejecución, estructura y modularidad son motivo suficiente para descartarlo, pues el resto de lenguajes que consideramos lo superan ampliamente en estos aspectos, ofreciendo PHP muy poco para compensar (su velocidad para proyectos mínimos).Puede sernos útil conocer PHP, pues existe mucho trabajo manteniendo código PHP (sin embargo, no se trata de un trabajo especialmente gratificante) y en algún momento nos puede ser útil. Pero debe ser erradicado lo antes posible.
* Perl[3]: durante mucho tiempo fue una de las mejores opciones disponibles, en realidad, una de las pocas viables. Una vez más, el resto de lenguajes de la lista le superan en virtudes sin que Perl ofrezca muchas ventajas propias. El mercado de Perl decae lentamente y cada vez se inician menos proyectos que lo utilicen.
-* JavaScript[4]: si bien deberemos conocer JavaScript para desarrollar efectivamente sobre la web, aún no lo considero una opción viable en el lado servidor. Tendremos que aprender JavaScript, pero el grueso del proyecto deberá ser siempre en otro de los lenguajes. Soy anti-aplicaciones web 100% Javascript, creo que su campo de aplicación es extremadamente limitado y presentan desventajas considerables, pero hay quien les encuentra virtudes
+* JavaScript[4]: si bien deberemos conocer JavaScript para desarrollar efectivamente sobre la web, aún no lo considero una opción viable en el lado servidor. Tendremos que aprender JavaScript, pero el grueso del proyecto deberá ser siempre en otro de los lenguajes. Soy anti-aplicaciones web 100% JavaScript, creo que su campo de aplicación es extremadamente limitado y presentan desventajas considerables, pero hay quien les encuentra virtudes
C#[5] y Visual Basic .NET[6] son dos opciones que el lector mismo puede escoger descartar o considerar- desarrollar razonablemente en ambos supone unos costes que yo prefiero no asumir (se necesitan licencias de Windows para el desarrollo y despliegue y las versiones gratuitas de Visual Studio tienen bastantes limitaciones)- a parte de que soy un firme creyente en que las herramientas de desarrollo deben ser libres y gratuitas. Si eso no supone un impedimento para el lector, puede aplicar mi opinión sobre Java, ambas plataformas son extremadamente similares; quizás .NET goce de herramientas más sencillas de utilizar inicialmente, el sistema base es más completo que el de Java pero el ecosistema goza de menor vida.
diff --git a/blog/content/2012/11/que-es-el-rpc.gmi b/blog/content/2012/11/que-es-el-rpc.gmi
index 717bea63..501b3b55 100644
--- a/blog/content/2012/11/que-es-el-rpc.gmi
+++ b/blog/content/2012/11/que-es-el-rpc.gmi
@@ -11,7 +11,7 @@ y que la suma se realice en el sistema remoto. A esto le llamaron llamada de pro
Sun implementó uno de los primeros sistemas de RPC para implementar el sistema de archivos distribuido NFS, y a lo largo del tiempo han ido apareciendo diferentes mecanismos de RPC para diferentes plataformas y necesidades.
-Con la popularización de la WWW, el protocolo HTTP y Javascript en los 90, pronto la gente comenzó a implementar comunicaciones entre sistema utilizándolos. Por ejemplo, una web podía exponer algunos de sus contenidos y funcionalidades en HTML para consumo humano, pero también exponerlos para consumo de otros sistemas. Mecanismos simples como poner una URL en la que si hacemos un POST http con unos argumentos, nos devuelve el resultado de una operación en un formato fácilmente parseable.
+Con la popularización de la WWW, el protocolo HTTP y JavaScript en los 90, pronto la gente comenzó a implementar comunicaciones entre sistema utilizándolos. Por ejemplo, una web podía exponer algunos de sus contenidos y funcionalidades en HTML para consumo humano, pero también exponerlos para consumo de otros sistemas. Mecanismos simples como poner una URL en la que si hacemos un POST http con unos argumentos, nos devuelve el resultado de una operación en un formato fácilmente parseable.
Pronto, uno de los padres fundadores del HTTP, procesó los principios fundamentales del HTTP y la WWW, en concreto que todo era una cuestión de URLs y acciones como GET/POST/PUT/DELETE que soporta el protocolo HTTP; cada URL representa un recurso y podemos expresar acciones mediante los "verbos" HTTP. A esto le llamó REST y supuso una perspectiva limpia y poderosa de lo que es la WWW.
diff --git a/blog/content/2013/09/recopilatorios-de-grandes-exitos.gmi b/blog/content/2013/09/recopilatorios-de-grandes-exitos.gmi
index c08c9e6b..6e76e871 100644
--- a/blog/content/2013/09/recopilatorios-de-grandes-exitos.gmi
+++ b/blog/content/2013/09/recopilatorios-de-grandes-exitos.gmi
@@ -18,9 +18,9 @@ En general, los compiladores suelen ser de lenguajes de mayor nivel a menor nive
En estos casos, la cosa se suele complicar mucho más porque una implementación naíf de un compilador a ensamblador suele redundar en programas que al ejecutarse son espectacularmente ineficientes. Conseguir ejecutables eficientes es un problema completo en sí mismo sobre el que se han escrito toneladas de libros.
-Sin embargo, recientemente son más habituales los compiladores que compilan lenguajes a cosas que no son ensamblador- por diversos motivos entre los que destaca que un compilador a ensamblador sólo es útil para una familia de CPUs, y pese a que la familia x86 de Intel y los ARM copan la mayor parte del mercado, sigue habiendo muchos otros procesadores en uso hoy en día. Por otra parte, plataformas como la máquina virtual Java, LLVM o incluso Javascript también son populares como destinos de los compiladores- en el caso de Java o LLVM por ser más simples para la generación de código sin sacrificar eficiencia, y en el caso de Javascript, por ser un destino particularmente útil ya que nos permite ejecutar el código compilado en un navegador.
+Sin embargo, recientemente son más habituales los compiladores que compilan lenguajes a cosas que no son ensamblador- por diversos motivos entre los que destaca que un compilador a ensamblador sólo es útil para una familia de CPUs, y pese a que la familia x86 de Intel y los ARM copan la mayor parte del mercado, sigue habiendo muchos otros procesadores en uso hoy en día. Por otra parte, plataformas como la máquina virtual Java, LLVM o incluso JavaScript también son populares como destinos de los compiladores- en el caso de Java o LLVM por ser más simples para la generación de código sin sacrificar eficiencia, y en el caso de JavaScript, por ser un destino particularmente útil ya que nos permite ejecutar el código compilado en un navegador.
-Tanto la JVM como la LLVM han sido diseñadas especialmente para este propósito, con lo que tienden a simplificarnos el proceso de compilación. En el caso de Javascript, pese a estar pensado con otros propósitos, proyectos como GWT o Emscripten han hecho grandes esfuerzos para hacer funcionar compiladores sobre Javascript. Mozilla incluso ha lanzado la iniciativa asm.js para definir un subconjunto de Javascript que sea práctico como plataforma a la que compilar de una manera eficiente.
+Tanto la JVM como la LLVM han sido diseñadas especialmente para este propósito, con lo que tienden a simplificarnos el proceso de compilación. En el caso de JavaScript, pese a estar pensado con otros propósitos, proyectos como GWT o Emscripten han hecho grandes esfuerzos para hacer funcionar compiladores sobre JavaScript. Mozilla incluso ha lanzado la iniciativa asm.js para definir un subconjunto de JavaScript que sea práctico como plataforma a la que compilar de una manera eficiente.
El proceso no se queda aquí, ya que una vez tenemos un lenguaje funcional con intérprete o compilador, siempre hay un interés en acelerarlo- tanto el proceso de compilación como la ejecución de los programas. Una vez más, se trata de un área complicada y sutilezas- se han llegado a técnicas extremadamente sofisticadas que incluso "aprenden" de la ejecución del programa y modifican su funci0namiento para adaptarse y mejorar "en vivo".
diff --git a/blog/content/2024/07/ovejas.gmi b/blog/content/2024/07/ovejas.gmi
index b6c948bb..4b19aca7 100644
--- a/blog/content/2024/07/ovejas.gmi
+++ b/blog/content/2024/07/ovejas.gmi
@@ -30,7 +30,7 @@ Voy soltando reflexiones chorras en el Fediverso que me da un poco de vergüenza
=> https://sansec.io/research/polyfill-supply-chain-attack Y cómo las optimizaciones innecesarias... (y otras manías mías)... nos exponen a peligrosos agujeros de seguridad.
-=> https://github.com/alexpdp7/alexpdp7/blob/master/misc/comprar-bajo-en-sodio.org Consejos para comprar sin sal, de alguien que NO es una autoridad (yo).
+=> ../../notas/comprar-bajo-en-sodio Consejos para comprar sin sal, de alguien que NO es una autoridad (yo).
=> https://en.m.wikipedia.org/wiki/The_Vanilla_Ice_Project The Vanilla Ice Project is an American reality television series on the DIY Network. It is hosted by construction contractor and rapper Rob Van Winkle, a.k.a. Vanilla Ice, who has significant experience with home improvement and real estate flipping.
diff --git a/blog/content/2026/03/me-gusta-el-futbol.gmi b/blog/content/2026/03/me-gusta-el-futbol.gmi
new file mode 100644
index 00000000..ae74e497
--- /dev/null
+++ b/blog/content/2026/03/me-gusta-el-futbol.gmi
@@ -0,0 +1,25 @@
+# 2026-03-19 Me gusta el fútbol
+
+Pero ya nunca veo partidos y básicamente sólo me entero de cosas porque no escaparía de los gritos ni en el fondo de la fosa de las Marianas. ¿Por qué?
+
+En 2011 escribí por aquí sobre Cruyff y la liga española[1].
+
+=> ../../2011/04/holandeses-voladores-ciclos-y-tendencias [1] Holandeses voladores, ciclos y tendencias
+
+Tengo algún recuerdo de Lineker, así que igual me acuerdo de 1986, pero pongamos que me aficioné con la primera liga de Cruyff.
+
+Entre la temporada 90-91 y la temporada 2003-2004, que es la última liga que ganó el Valencia, de esas catorce ligas hubo cuatro ligas que ganadas por tres equipos que no son ni Madrid ni Barcelona. (Dos del Valencia, una del Atlético de Madrid y otra para el Depor.)
+
+A partir de la 2004-2005, el Atlético de Madrid ha ganado dos ligas y las otras veinte, todas para Madrid y Barcelona.
+
+Por mucho que mi equipo preferido haya ganado 12 ligas frente a 7 de su eterno rival, que la competición con más partidos sea una moneda al aire entre los mismos dos equipos siempre (con el canto para el mismo tercer equipo siempre) me parece un soberano aburrimiento. Desde que el Valencia quedó tercero en la liga 2011-2012, sólo el Girona en la 2023-2024 ha conseguido colarse entre los tres primeros (para quedar el decimosexto la siguiente temporada). En mi opinión, la imprevisibilidad es uno de los factores que más contribuye al entretenimiento.
+
+Además, el recuerdo quizá endulzado por el tiempo que tengo del periodismo deportivo (por ejemplo, El Día Después con Michael Robinson, que echó el cierre en 2005) ha sido sustituido por el ruido y la furia vacíos a la que yo atribuyo su comienzo a la temporada 2009-2010. (Más o menos por esa época se labran más polarizaciones, curiosamente. Casualmente, es 2007 cuando Facebook superó a MySpace en como la plataforma social más popular del mundo y Twitter tuvo su explosión durante la conferencia South by Southwest Interactive.)
+
+Pero lo que me llama más la atención es lo absurdamente caro que te puede salir ver fútbol. Mirad lo que os costaría ver los 90 minutos semanales de vuestro equipo favorito en liga y comparad con lo que os costaría tener streaming hasta aburriros de cualquier otra cosa.
+
+Sigo considerando que el fútbol no se lo come todo por casualidad. (Aunque me parece que la introducción del videoarbitraje allá por 2017 en mi opinión rompe una de sus virtudes: que los aficionados juegan prácticamente a lo mismo que los equipos de primer nivel.) Pero mi consejo es que hay deporte para aburrir con la misma o más emoción, gratis y sin la toxicidad del fútbol de primer nivel al que estaba tan aficionado de joven.
+
+(Personalmente, principalmente veo ping pong y baloncesto, que son los dos deportes que he practicado más, y casi con cualquier cosa libremente disponible sin piratear en Internet, aunque por diversos motivos, raramente estoy viendo nada de más de diez minutos.)
+
+Apéndice 2026-04-05: menuda turra que solté sin mencionar lo más reciente. Que ahora cada vez que hay fútbol, cortan la mitad de Internet. El bonus es que es un conflicto entre varias partes en el que todas las partes me caen mal. El fútbol se lo come todo, huid si podéis.
diff --git a/blog/content/2026/03/notas.gmi b/blog/content/2026/03/notas.gmi
new file mode 100644
index 00000000..0a1da7b0
--- /dev/null
+++ b/blog/content/2026/03/notas.gmi
@@ -0,0 +1,32 @@
+# 2026-03-01 Notas
+
+Hoy tocaba escribir en el blog, pero en vez de ello me he dedicado a migrar más contenido (principalmente "ensayos") a esta web.
+
+Durante mucho tiempo, metía ficheros de texto (mayoritariamente en Markdown) en mi "monorepo" Git personal. (El monorepo es un amalgama de un montón de cosas, incluido este blog y muchas más cosas.) Esta técnica no funciona mal y es muy cómoda, pero me ataba demasiado a GitHub.
+
+Ahora podéis leer ese contenido en:
+
+=> ../../notes/ Notes (en inglés)
+=> ../../notas/ Notas (en español)
+
+Con lo que ahora todo tiene URLs controladas por un servidor.
+
+Además, he convertido todo (a manopla) al formato gemtext de Gemini. gemtext es mucho más limitado que Markdown, con lo que he tenido que simplificar un poco el formato. Pero creo que esta "sencillez obligada" me ayuda a estructurar las cosas de una manera más simple y más adaptable a distintos formatos. (Como que por ejemplo, podéis leer todo usando un navegador de Gemini, lo que ofrece en mi opinión muchas ventajas.)
+
+Tras borrar también bastante contenido obsoleto o de baja calidad, creo que ya no queda casi nada susceptible de pasarse a gemtext. Todavía hay algo material, pero es mayormente:
+
+* Listados en formato Org con mucha jerarquía que no funcionarían muy bien en gemtext.
+* Un par de artículos que debería actualizar y modernizar bastante... cosa que seguramente no haré a corto plazo y esperaré a que necesiten un remodelado profundo.
+
+Con esto creo que cierro una primera fase de la iteración de este sitio web (ahora cápsula y no sólo blog). Curiosamente he "perdido" funcionalidad:
+
+* Falta navegabilidad; básicamente sólo podemos desandar nuestros pasos en la nueva web usando el botón de "atrás" de nuestro navegador.
+* He perdido la editabilidad que me daba GitHub; antes los textos en GitHub tenían un botón editar la mar de majo y las entradas del blog tenían un enlace para editarlas en GitHub.
+
+Lo primero creo que tengo maneras de resolverlas que intentaré aplicar igual a corto plazo.
+
+Lo segundo me fastidia mucho más y no se me ocurre una buena manera de subsanarlo. Podría seguir usando las funcionalidades de GitHub, pero claro, estoy intentando reducir mi dependencia. Podría usar software equivalente a GitHub, pero me estoy inclinando por alternativas mucho más sencillas para hospedar Git que no tienen funcionalidad equivalente (y que me resultan muy atractivas por otros motivos).
+
+Para cerrar comentaré que ahora mismo el contenido de esta web es más o menos de 700.000 palabras. A veces en mi cabeza le llamo a esto "mi universo cinemático", porque no es más que una película que me he montado en la cabeza durante más de un cuarto de siglo. Por suerte o por desgracia no soy Jack Kerouac[1], pero al menos os diré que tanta verborrea me entretiene y creo que me enriquece. Os animo a montar vuestros propios universos cinemáticos.
+
+=> https://sabr.org/journal/article/jack-kerouac-the-beat-of-fantasy-baseball/ [1] Jack Kerouac: The Beat of Fantasy Baseball
diff --git a/blog/content/2026/03/peligros-de-bolsillo.gmi b/blog/content/2026/03/peligros-de-bolsillo.gmi
new file mode 100644
index 00000000..2464ed43
--- /dev/null
+++ b/blog/content/2026/03/peligros-de-bolsillo.gmi
@@ -0,0 +1,45 @@
+# 2026-03-20 Peligros de bolsillo
+
+Google lleva un tiempo intentando controlar los programas que uno puede ejecutar en su móvil Android. La idea es que toda aplicación deba estar aprobada por Google, lo que incluye que Google pueda identificar a la persona física responsable de la aplicación.
+
+Esta es la enésima versión de cosas de las que ya he hablado varias veces por aquí:
+
+=> ../../2025/02/el-lento-adios-a-la-magia-de-los-ordenadores El lento adiós a la magia de los ordenadores (2025, sobre que nos limiten en el uso de nuestros dispositivos)
+
+=> ../../2015/12/por-que-no-uso-productos-apple Por qué no uso productos Apple (2015, sobre cómo Apple me parece de lo peor en este sentido)
+
+Pero la verdad, las medidas en concreto que Google propone ahora no me gustan, pero tampoco se me ocurre nada mejor.
+
+Básicamente, la opción que va a dar Google para que podamos ejecutar aplicaciones que no controlan parece draconiana: entre otras cosas, el botón para habilitar esta funcionalidad tardará un día entero en tomar efecto (y además estará bastante escondido).
+
+Para mí, esto es bastante más satisfactorio que lo que proponían antes y no se me ocurre una opción mejor.
+
+Los autores de F-Droid, un excelente sistema para distribuir aplicaciones de código libre en Android de donde instalo bastantes aplicaciones, firman la siguiente carta abierta en contra de estas medidas:
+
+=> https://keepandroidopen.org/es/ Keep Android Open
+
+Enlazo a la traducción en español que todavía no está actualizada con la respuesta a la última propuesta de Google, donde rechazan la propuesta. La verdad que su contraargumento principal es que todavía no se puede verificar que Google hará esto y que Google puede volver a cambiar las reglas cuando quiera. Ambas son ciertas y estoy de acuerdo, pero también dan énfasis a que las medidas son un poco draconianas.
+
+En mi opinión, Google está reaccionando a una amenaza concreta. Según Google, hay muchos entes malignos haciendo llamadas telefónicas a gente y engañándoles para que instalen aplicaciones maliciosas en su teléfono. Si esto es cierto, ¿qué hacemos?
+
+Antes de responder a esta pregunta, en este punto creo que debemos preguntarnos varias cosas.
+
+Lo primero es que cómo hemos llegado a esta situación. Instalar una aplicación maliciosa en tu móvil puede ser muy peligroso. Por muchas medidas de protección que se pongan, es inevitable que pongamos en peligro cualquier cosa a la que podamos acceder por ese móvil. Y eso, por muchos motivos distintos, incluye muchísimas cosas importantes.
+
+Y hoy en día, estamos casi obligados a usar en móvil y casi obligados a manejar cosas sensibles a través de él. Por ejemplo, hoy en día el canal de comunicación con muchos de nuestros allegados es por (mal que me pese) WhatsApp. Muchos bancos restringen el acceso a mucha funcionalidad útil a su aplicación del móvil. Incluso algunos organismos oficiales nos hacen pasar por el móvil.
+
+(Y ojo, esto no es para nada algo exclusivo del móvil. Esto ya no pasa tanto con los ordenadores de escritorio y portátiles porque simplemente están muriendo. Creo que todo el mundo tiene móvil y cada vez menos gente tiene ordenadores porque los móviles son más sencillos y requieren menos esfuerzo de uso que los ordenadores tradicionales.)
+
+Entonces viene la pregunta de si, siendo el móvil un dispositivo imprescindible para la vida moderna, todo el mundo debe aprender a manejarlo con responsabilidad para estar protegido.
+
+Yo creo que es mejor si podemos dar a la gente una opción para poder comunicarse por WhatsApp y gestionar su dinero de una manera segura sin tener que dedicar un esfuerzo importante en formación para no correr un peligro real y significativo.
+
+Y en ese punto, creo que las medidas de Google pueden ser útiles y no veo opciones mejores si quiero dar a la gente la opción que acabo de mencionar. Porque me creo que ese bloqueo de un día puede ser altamente efectivo.
+
+Pero que yo defienda estas medidas no quiere decir que esté contento con ellas. Que hayamos llegado hasta aquí es un problema, y que la gente que está dispuesta a aprender a protegerse tenga que aceptar todas las cosas malas que vienen en este paquete me parece terrible.
+
+Porque en efecto, todos estamos prácticamente obligados a usar un dispositivo totalmente controlado por Apple o Google, dos empresas privadas cuyo objetivo es ganar dinero. Y esto es una tragedia por innumerables motivos, tanto obvios como sutiles.
+
+En mi opinión, lo que necesitamos es que todo servicio "vital" se pueda usar sin pasar por el control de ninguna gran empresa privada. Si un sistema de mensajería es importante para vivir en sociedad, debe ser interoperable como es (o debería ser) el correo electrónico. Para usar WhatsApp tengo que usar un dispositivo controlado por Google o Apple y ejecutar en este dispositivo una aplicación controlada por Facebook. Con el correo electrónico (con ciertas notas al pie), puedo usar el dispositivo que me dé la gana, con el programa que me dé la gana y escogiendo el proveedor de servicio que me dé la gana. (E incluso, con más notas al pie, me lo puedo guisar todo yo mismo.)
+
+Si tenemos esto, las restricciones de Google me dan bastante igual, porque creo que bajo estas condiciones, no tendré un teléfono controlado por Google como lo tengo hoy en día, o al menos tendré la opción. Y si no tenemos esto, creo que tener que esperar un día para poder instalar aplicaciones de F-Droid quizá sea más bueno que malo.
diff --git a/blog/content/2026/03/this-was-supposed-to-be-the-future.gmi b/blog/content/2026/03/this-was-supposed-to-be-the-future.gmi
new file mode 100644
index 00000000..3c97e5b4
--- /dev/null
+++ b/blog/content/2026/03/this-was-supposed-to-be-the-future.gmi
@@ -0,0 +1,27 @@
+# 2026-03-04 This was supposed to be the future
+
+(No sé cuán antigua es una camiseta que creo que todavía conservo con este mensaje. Mucho texto ya es ilegible.)
+
+Estaba oyendo hoy otra cantinela sobre las nuevas maravillas de Apple. Nada nuevo bajo el sol; la gente sigue dándole dinero y elogios a dos manos a Apple, y a mí siguen sin gustarme. He pensado en escribir sobre el tema, pero me he puesto a repasar lo que escribí en 2015 sobre el tema:
+
+=> https://alex.corcoles.net/2015/12/por-que-no-uso-productos-apple Por qué no uso productos Apple
+
+Y me ha perturbado que tendría que cambiar muy poco para ponerlo al día. Sí, ya estoy en una edad donde muy probablemente me queda menos por delante que lo que llevo a mis espaldas, y que por tanto, mi capacidad para corregir mis errores está todavía más mermada, pero si estoy equivocado, me parece cuanto menos curioso que esté equivocado de la misma manera durante más de una década.
+
+Esto me ha llevado a caer en que este mundo que en cada momento nos parece que cada vez va más rápido, quizá sólo esté revolucionando muy rápido sin moverse del sitio. Porque hace un año recordé la que quizá fue la gran revolución informática en mi era, hace un cuarto de siglo:
+
+=> https://alex.corcoles.net/2025/01/los-sistemas-operativos-y-un-amanecer-de-internet Los sistemas operativos y un amanecer de Internet
+
+Alrededor del cambio de milenio fuimos acostumbrándonos a que nuestros ordenadores estuviesen siempre conectados a Internet. Unos años más tarde (muchos lo datarían en 2007; yo creo que fue un poco antes), la conexión a Internet constante comenzó a estar con nosotros incluso lejos de la mesa del ordenador.
+
+Tecleo esto en un ordenador comprado en 2017 que no distingo demasiado del pepino que la semana pasada me pusieron en el curro. Mi BlackBerry de 2011 me parece mejor en algunos aspectos a mi Pixel 9A del año pasado.
+
+Pero lo peor de todo es que la parte de las involuciones ya fue objeto de burla en 2010... ¡porque en 2003 llamaban locos a los que lo veían venir!
+
+=> https://www.explainxkcd.com/wiki/index.php/743 Infrastructures
+
+En los últimos años, claro, es difícil resistir al asedio de lo que muchos dicen que es la nueva revolución. Aunque creo que ya tocaría, veo poco positivo y mucho negativo en el tema del lustro, quizá década.
+
+(Quiero clarificar que me refiero estrictamente a lo mío; por supuesto observo grandísimas innovaciones muchísimo más relevantes que lo que podemos hacer todos en vez de coger un libro.)
+
+¿Soy un señor mayor que no entiende los tiempos modernos? ¿Me estoy perdiendo algo? ¿Cuándo podremos celebrar algo como lo que celebrábamos hace un par de décadas?
diff --git a/blog/content/2026/04/breve-e-incompleta-historia-del-desarrollo-web.gmi b/blog/content/2026/04/breve-e-incompleta-historia-del-desarrollo-web.gmi
new file mode 100644
index 00000000..ed19cb36
--- /dev/null
+++ b/blog/content/2026/04/breve-e-incompleta-historia-del-desarrollo-web.gmi
@@ -0,0 +1,93 @@
+# 2026-04-05 Breve e incompleta historia del desarrollo web
+
+El primer navegador y servidor web aparecieron en 1990, pero hasta 1993 la web era mayormente documentos estáticos; los servidores web tiraban de una carpeta con ficheros HTML con los que se construían las primeras páginas web. Cada vez que ibas a una dirección de la web, veías siempre el mismo documento, que podía contener enlaces a otros documentos.
+
+Es decir, nada de teclear un término de búsqueda en un formulario y obtener un listado de sitios web, ni por supuesto nada muchísimo más completo como poder escribir correo o publicar nada en la web usando sólo un navegador.
+
+(¡Esto no es del todo cierto! WorldWideWeb, el primer navegador, incorporaba un editor de páginas web. Pero sólo servía para editar el sitio web hospedado en el mismo ordenador donde ejecutábamos el navegador.)
+
+Últimamente pienso que nos debíamos haber quedado ahí, pero en 1993 apareció el "Common Gateway Interface", unas siglas bastante inescrutables excepto por el "Interface", con lo que lo dejaremos en CGI a secas.
+
+El CGI permite que un servidor web responda a la petición de un navegador no yendo a buscar un documento HTML dentro del ordenador, sino ejecutando un programa que genere la respuesta.
+
+Si os atrevéis con el terminal y tenéis Python instalado, podéis viajar al pasado siguiendo los siguientes pasos:
+
+1. Cread un directorio vacío.
+
+2. Cread un directorio con el nombre cgi-bin dentro del primer directorio.
+
+3. Cread un archivo con el nombre hola dentro del directorio cgi-bin con el siguiente contenido:
+
+```
+#!/bin/sh
+
+echo Content-type: text/html
+echo
+echo Hoy es $(date)
+```
+
+4. Haced que este archivo sea ejecutable con el siguiente comando:
+
+```
+chmod ugo+x cgi-bin/hola
+```
+
+5. Ejecutad un servidor web apropiado:
+
+```
+python3 -m http.server --cgi
+```
+
+6. Visitad http://0.0.0.0:8000/cgi-bin/hola y comprobad el resultado. Recargad la página varias veces y veréis que se actualiza la fecha, con lo que ya tenéis una página dinámica.
+
+Con este invento relativamente sencillo ya prácticamente podemos llegar a tener gran parte de la web hasta 2004 o así.
+
+El CGI es sencillo, pero algo tedioso. Cuando un programador escribe más de dos o tres programas CGI, se da cuenta de que se repiten los mismos patrones una y otra vez. El ejemplo anterior utiliza el lenguaje shell, pero la historia que conservamos parece indicar que el lenguaje de programación Perl fue de los más usados en los albores del CGI pues era de las maneras más convenientes de reutilizar código para implementar sitios web dinámicos mediante CGI.
+
+Seguramente uno de los primeros módulos para escribir CGI en Perl es CGI.pm, del que la versión más antigua que se conserva en el principal repositorio de código Perl es la 2.10 de 1995.
+
+Perl es un lenguaje de programación de propósito general que apareció en 1987, mucho antes que la web y el CGI.
+
+Allá por 1993, un programador comenzó a escribir un lenguaje de programación con el propósito de implementar su página personal. La primera versión oficial salió en 1997, con el nombre PHP.
+
+A diferencia del CGI, hoy en día es muy complejo reproducir la experiencia exacta de desarrollo de las primeras versiones de PHP, pero si tenéis PHP instalado, podéis hacer algo relativamente similar creando un archivo con el nombre index.php y el siguiente contenido:
+
+```
+<form>
+ <label>a: <input name="a" type="number">
+ <label>b: <input name="b" type="number">
+ <input type="submit">
+</form>
+
+<?php
+ if ($_GET['a'] && $_GET['b']) {
+ echo $_GET['a'] + $_GET['b'];
+ }
+?>
+```
+
+Luego, ejecutad el siguiente comando:
+
+```
+php -S 0.0.0.0:8000
+```
+
+Entonces visitad http://0.0.0.0:8000 con vuestro navegador, introducid dos números en los campos de entrada, pulsad el botón y veréis la suma.
+
+Implementar la misma funcionalidad con CGI, incluso reutilizando código como CGI.pm, es bastante más tedioso y requiere más conocimientos que usar PHP. Por eso el ejemplo que os he puesto de CGI es algo tan inútil.
+
+Yo mismo descubrí PHP por el año 2000 y, como muchísima otra gente, quedé hipnotizado por lo que en el momento era una de las mejores maneras de hacer cosas útiles con un ordenador para otra gente.
+
+A diferencia del CGI, mediante el cual nacieron las webs dinámicas, PHP no trae nada nuevo directamente al usuario de la web, sólo intenta simplificar la vida al programador. Pero facilitando la vida al programador, seguramente PHP aceleró el desarrollo web permitiendo a los programadores traer webs más útiles.
+
+Como muchos sabréis, ni PHP ni Perl han sido el final. Hoy en día, prácticamente todos los lenguajes de programación se usan para desarrollo web, con todo tipo de código reutilizable que cada vez nos aleja más del CGI.
+
+Paralelamente al desarrollo de CGI, PHP y las webs dinámicas, en 1995 apareció el primer navegador con JavaScript. A diferencia de los ejemplos anteriores, donde el código que os he puesto se ejecuta en el servidor web, JavaScript se ejecuta en vuestro navegador. Esto permite otro tipo de interacciones que ejemplificaremos con Google Maps lanzado en 2005, que ya nos permitía desplazarnos por el mapa de una manera mucho más interactiva que lo que nos permite ninguna web dinámica sin JavaScript.
+
+Casi tres décadas más tarde, curiosamente el CGI sigue existiendo pero la mayoría del desarrollo web no tiene casi nada que ver con el de 1995 con PHP y JavaScript. En mi opinión, los ejemplos que os he puesto anteriormente omiten algo de tedio, pero son mayormente representativos de esas maneras de desarrollo, mientras que me marea simplemente pensar en poner un ejemplo mínimo de desarrollo web moderno.
+
+El fenómeno que observamos en mi opinión se repite por todo el ámbito del desarrollo del software: cada vez el paso inicial de desarrollo nos lleva más lejos, pero también es más difícil de abarcar.
+
+Lo que me ha llevado a escribir este artículo es que esta mañana he decidido jugar con una idea que tenía desde hace tiempo en la cabeza, y mi prototipo han sido 100 líneas de Python con WSGI, un descendiente directo del CGI. Para otras ideas sin duda me habría ido a algo mucho más moderno y potente, pero para esta en concreto dudo que hubiese encontrado un camino más corto que las maneras más primitivas del desarrollo web. Pero, ¿habría tomado este camino si me hubiese iniciado en la programación web allá por 2013 con la primera versión de React?
+
+Yo creo que no. Y quizá ni lo hubiese intentado.
diff --git a/blog/content/2026/04/el-enemigo-en-casa.gmi b/blog/content/2026/04/el-enemigo-en-casa.gmi
new file mode 100644
index 00000000..36eb9fa3
--- /dev/null
+++ b/blog/content/2026/04/el-enemigo-en-casa.gmi
@@ -0,0 +1,35 @@
+# 2026-04-08 El enemigo en casa
+
+Allá por 2013 cogí una webcam (diría que una de Playstation) y monté un pequeño sistema de videovigilancia para poder echar un ojo a la gata desde fuera de casa. Un programilla que sabía detectar movimiento y unos cuantos scripts hacían que recibiese un email con un vídeo cuando la gata pasaba delante de la cámara. Fue algo bastante práctico aunque dejé de usarlo en algún momento.
+
+Desde luego no fui un pionero, pero creo que poco después el tema de la domótica se puso muy de moda. No sólo entre la gente más acostumbrada a trastear, sino que además se sumó el público más general, en parte gracias a que comenzaron a aparecer muchos aparatos para estos menesteres con costes más bien asequibles que suponían mucha menos complicación que mi chapuza sujeta a base de chicle.
+
+Pero este boom se vio acompañado de mucha gente obsesionándose con aprender a marchas forzadas temas de seguridad informática para proteger su red doméstica por si estos nuevos y convenientes aparatos resultaban ser malignos. Ni trabajando en una empresa con muchos técnicos en redes había oído mencionar tanto el término vLAN.
+
+La verdad que en su momento tanta paranoia me parecía un poco exagerada. Mi razonamiento era más bien que a pesar de unos cuantos incidentes preocupantes, simplemente bastaría con usar productos de marcas más o menos establecidas con cierta necesidad de mantener su reputación. Es decir, no meter en casa un aparato de quién sabe qué empresa que igual desaparece al mes que viene debería bastar para dormir tranquilo.
+
+(En 2024 me hice con un par de aparatos de este tipo que sigo teniendo conectados y con los que realmente no he tomado muchas precauciones. Pero seleccioné cuidadosamente la marca que elegí, entre otros motivos porque me daba bastante confianza.)
+
+Además, incluso aunque nos colasen un dispositivo malicioso, tampoco me parecía una amenaza tan seria.
+
+Creo que en ese momento mi razonamiento ya no era muy acertado, pero además últimamente hay acontecimientos que me hacen replantearme mucho más mi postura y mis recomendaciones.
+
+Los ataques de denegación de servicio no son algo nuevo; desde hace mucho tiempo hay actores malignos que bombardean servicios de Internet con una avalancha de peticiones que pueden incluso tumbar el servicio y causar muchos problemas a su operador. Sin embargo, hasta hace poco estos ataques tenían más bien pocos objetivos y en general se trataba de objetivos que ya tenían que preocuparse de defenderse ante todo tipo de hostilidades, y en la mayoría de casos, podían disponer de recursos para protegerse.
+
+Sin embargo, en tiempos recientes estos ataques se han expandido significativamente. Incluso servicios muy pequeños hospedados por particulares reciben ataques de este tipo constantemente. Incluso yo mismo he recibido cierta carga que por suerte no ha tenido mayores consecuencias que costarme un par de euros mensuales, pero nada en comparación con lo que veo sufrir a otros.
+
+Es especialmente difícil defenderse de estos ataques; es muy complicado identificar y bloquear el tráfico malicioso, pues proviene de muchísimas conexiones a Internet domésticas.
+
+Que es precisamente lo que se conseguiría controlando una buena cantidad de dispositivos de domótica maliciosos.
+
+(Sí es cierto que no es la única manera de conseguirlo. Hay empresas que ofrecen este tipo de "acceso a Internet", en general porque controlan aplicaciones para móviles "dudosas" y que por tanto pueden canalizar tráfico a través de los dispositivos en los que alguien ha instalado estas aplicaciones.)
+
+Lo curioso del caso es que las precauciones que se pusieron de moda para controlar los dispositivos de domótica en general no serán efectivas para prevenir esto, pues estas medidas se centraban en aislar estos dispositivos para que no pudiesen acceder al resto de nuestra red. Pero para usarlos para lanzar un ataque no hace falta que se conecten a otros dispositivos de nuestra red; basta con que puedan conectarse a Internet.
+
+Y la mayoría de estos dispositivos necesitan conectarse a Internet para ofrecer su modo de funcionamiento "fácil" en los que usan la infraestructura del fabricante en vez de requerirnos mantener nuestra propia infraestructura. (Es decir, en el caso de una cámara, por ejemplo, la cámara va enviando el vídeo al servicio del fabricante donde queda almacenado, y a posteriori nosotros nos conectamos al servicio del fabricante para ver las grabaciones.) Y por otras modernidades, a no ser que el fabricante del aparato lo facilite expresamente, no es tan fácil restringir la conexión a Internet de estos aparatos para que sólo se puedan conectar a lo imprescindible para su funcionamiento y no puedan usarse para lanzar ataques de denegación de servicio distribuidos.
+
+Con lo que yo antes hubiese recomendado despreocuparse un poco del tema, pero ahora me veo obligado a recomendar mucha cautela.
+
+Lamentablemente, la única alternativa segura (aparte de no usar estos dispositivos) sería usar sólo aparatos que no necesiten conexión a Internet para funcionar, pero esto necesariamente implica mucho más trabajo por nuestra parte, e incluso en ocasiones no será viable.
+
+Fuera de eso, sólo puedo insistir en mi consejo de limitarse a fabricantes que tengan más que perder que que ganar participando en actividades maliciosas. Pero dentro de esto seguimos teniendo un riesgo real de facilitar estos ataques y no es fácil mitigar este riesgo.
diff --git a/blog/content/gemini.gmi b/blog/content/gemini.gmi
new file mode 100644
index 00000000..b79340d9
--- /dev/null
+++ b/blog/content/gemini.gmi
@@ -0,0 +1,30 @@
+# Gemini
+
+This is my Gemini start page. I was unhappy with not having synchronized bookmarks in my Gemini clients, until I realized I could just publish a start page.
+
+The links below are Gemini links. You need a Gemini client to open them.
+
+## Aggregators
+
+=> gemini://cosmos.skyjake.fi/ Cosmos
+=> gemini://caracolito.mooo.com/deriva/ bot en deriva
+=> gemini://planet-gemini.fr Planet Gemini Francophonie - Agrégateur de capsules francophone
+
+## Search engines
+
+=> gemini://kennedy.gemi.dev/ Kennedy: Search Gemini Space
+=> gemini://tlgs.one/ TLGS - "Totally Legit" Gemini Search
+=> gemini://cdg.thegonz.net/ Collaborative Directory of Geminispace
+
+## Other stuff of interest
+
+The links below are web links, for those who do not have a Gemini client yet.
+
+### Clients
+
+=> https://gmi.skyjake.fi/lagrange/ Lagrange
+=> https://github.com/makew0rld/amfora Amfora
+
+### Other software
+
+=> https://ñix.es/cgit/alex/coppewebite.git/about/ Coppewebite is what I use to generate and serve this capsule
diff --git a/blog/content/notas/comprar-bajo-en-sodio.gmi b/blog/content/notas/comprar-bajo-en-sodio.gmi
new file mode 100644
index 00000000..9af1e1ea
--- /dev/null
+++ b/blog/content/notas/comprar-bajo-en-sodio.gmi
@@ -0,0 +1,176 @@
+# Comprar bajo en sodio
+
+Es complicado comer una dieta baja en sal. Este documento contiene productos que:
+
+* Podríamos pensar que tienen más cantidad de sal, pero en realidad no tienen tanta. (Intento comer cosas con menos de 1g sal/100g, preferiblemente menos de 0,5g.)
+* Cosas que pese a no tener sal, son más sabrosas de lo que uno esperaría.
+
+DISCLAIMER: No soy médico ni nutricionista. Contrastad todo lo que recomiendo aquí, que no son más que consejos y sugerencias sobre mis experiencias, sin ningún rigor médico. El propósito de esta página es compartir ideas, no dar consejo médico/nutricional.
+
+ATENCIÓN: Listo específicamente el producto preciso y la cantidad de sal aplica solamente a ese producto. Dentro de productos simiares, el contenido en sal puede variar muchísimo. Prestad siempre atención al comprar para revisar la cantidad de sal, que puede no coincidir con la descrita aquí. La sal no es la única fuente de sodio, revisad si lo que coméis lleva otras sustancias con sodio como el glutamato monosódico (o MSG).
+
+## Recursos interesantes
+
+=> https://world.openfoodfacts.org Open Food Facts
+Sorprendentemente, contiene la mayoría de productos que he intentado buscar
+
+## Productos
+
+### Quesos
+
+=> https://www.compraonline.alcampo.es/products/auchan-queso-arz%C3%BAa-ulloa-d-o-p-500-g-producto-alcampo/91158 AUCHAN Queso Arzúa Ulloa D.O.P. 500 g.
+Sal: 0,39/100g
+
+Este queso es el motivo principal para crear este documento. Este tipo de quesos parecen en general bajos en sal, y el de Alcampo tiene especialmente poca. Sin embargo, me gusta mucho (y me hace dudar que la información nutricional sea correcta).
+
+Los quesos tipo tetilla también tienen poca sal, pero más que este.
+
+Burratas
+Sal: Mercadona 0,61/100g
+
+Las burratas también tienen poca sal, y pueden ayudar a dar algo de vida a algún plato.
+
+Mascarpone
+Sal: Mercadona 0,11/100g
+
+El mascarpone puede servir para hacer más cremoso algún plato, sin añadir demasiada sal.
+
+Mercadona - Queso untar light de vaca Hacendado
+Sal: 0,75/100g
+
+El queso de untar light de Mercadona tiene bastante más sal que otros quesos apuntados aquí, pero menos que la mayoría de quesos de untar. Puede ser un capricho ocasional.
+
+### Pan
+
+Mercadona - Piquitos bajos en sal Hacendado
+Sal: 0,22/100g
+
+Para su bajo contenido en sal, los picos bajos en sal de Mercadona son muy sabrosos y pueden servir para picar.
+
+Alcampo - Pan Milagros de molde integral sin corteza
+Sal: 0,76/100g
+
+En general el pan de molde suele llevar bastante sal, aunque hay excepciones.
+
+Tortillas de trigo integrales Auchan
+Sal: 0,88/100g
+
+Las tortillas de trigo suelen llevar más sal, no he encontrado con menos que estas.
+
+### Conservas
+
+Hígado de bacalao ahumado
+Sal: Auchan 0,54/100g
+
+El hígado de bacalao es graso, pero sabroso pese a no tener mucha sal. Puede servir para aderezar un plato.
+
+Atún en aceite bajo en sal
+Sal: Calvo 0,16/100g, Mercadona 0,3/100g
+
+Aunque abuso un poco del atún bajo en sal, sirve para dar un poco de vida a muchos platos. Ojo, que la cantidad de sal puede variar mucho.
+
+Lomos de bonito en aceite
+Sal: Miau 0,59/100g
+
+Los lomos de bonito en aceite, generalmente en tarros de cristal, llevan más sal que el atún en aceite bajo en sal, pero algunos no llevan mucho más y me parecen más sabrosos. Ojo, que la cantidad de sal puede variar mucho (por ejemplo el de Mercadona se va a 0,84/100g).
+
+### Platos preparados
+
+Litoral Fabada asturiana -30% sal y grasa
+Sal: 0,6/100g
+
+Tortilla de patata sin cebolla Auchan
+Sal: 0,91/100g
+
+Carrilera en salsa Carretilla
+Sal: 0.68/100g
+
+Pizza cuatro quesos Auchan
+Sal: 0.9/100g
+
+Pizza mozzarella Auchan
+Sal: 0.83/100g
+
+Aunque los platos preparados suelen tener bastante sal, a veces alguno lleva menos de lo que parece.
+
+### Pasta
+
+Pasta rellena
+Sal: Giovanni Rana ravioli costillas a la barbacoa 0,6/100g
+
+Muchas pastas rellenas tienen bastante sal, pero al haber bastante variedad, a veces nos encontramos sorpresas. No es ideal comer pasta rellena sólo hervida, pero de esta manera podemos reducir nuestro consumo de sal.
+
+### Cereales
+
+Weetabix Crispy Minis Choco
+Sal: 0,16/100g
+
+De los mejores cereales con nutri-score A, y con menos sal que otros (e.g. los Choco Krispies de Kellogg's tienen 0,67/100g de sal). (Nota: recientemente pasaron a tener nutri-score B, aunque la información nutricional no cambió.) Si queremos desayunar cereales, estos nos permiten ahorrar un poco de sodio.
+
+### Patatas
+
+Patatas microondas
+Sal: Mercadona 0,015/100g
+
+Las patatas de microondas se preparan rápido y combinadas con otros productos, pueden arreglar una comida rápida. En general, suelen llevar muy poca sal y están buenas sin añadir más, aunque es recomendable aderezarlas con otro producto sin sal.
+
+### Patatas fritas y snacks
+
+Patatas prefritas congeladas
+Sal: Mercadona Patatas prefritas Waffle fries Lambweston ultracongeladas 0,5/100g
+
+Muchas patatas prefritas congeladas no llevan mucha sal y se pueden comer perfectamente solas.
+
+Patatas fritas
+Sal: Mercadona bajas en sal 0,011/100g, Patatas Fritas en Aceite de Oliva "Arte Fritas" 0.5/100g, Patatas sabor picante Alcampo 0,8/100g
+
+Las patatas fritas sin sal están más buenas de lo que a priori cabría esperar, y pueden ser un aperitivo más que adecuado. Y no son frecuentes, pero algunas patatas fritas llevan menos sal que la mayoría.
+
+Preparados de patata
+Sal: Mercadona patatas bravas con allioli y salsa picante 0,37/100g, Mercadona patatas con allioli 0,6/100g
+
+Aunque llevan más sal que otros productos de esta categoría, siguen teniendo no mucha sal y pueden ser otro aperitivo.
+
+Tortitas
+Sal: Ecocesta 0,5/100g
+
+En general, las tortitas (de arroz, maíz, etc.) *no* son bajas en sal. Sin embargo, se pueden encontrar algunas con menos sal. En el Alcampo que uso, hay una sección con tortitas que todas tienen bastante sal, pero en la sección de alimentos dietéticos tienen las tortitas Ecocesta con menos sal.
+
+Tortolines chifles con sal
+Sal: 0,5/100g
+
+Estos chips de plátano tienen algo menos de sal que la mayoría de snacks salados que encontramos en el supermercado.
+
+### Arroces
+
+Arroz cocido basmati Sabroz Brillante
+Sal: 0,35/100g
+
+Los vasitos de arroz pueden variar bastante en sal, pero los Sabroz no llevan muchísima. Se pueden combinar con otros productos para arreglar una comida rápida.
+
+### Salsas
+
+Mayonesa Calvé sabor casero
+Sal: 0,73/100g
+
+La mayoría de mayonesas tienen bastante sal, pero hay alguna excepción.
+
+Patak's Tikka Masala
+Sal: 0,72/100g
+
+Una salsa india con una cantidad de sal moderada que puede aderezar arroz o carne.
+
+Alioli Prima
+Sal: 0,72/100g
+
+El alioli Prima no es particularmente bueno, pero no he encontrado otro allioli que no tenga mucha más sal.
+
+Nacho Cheese Doritos
+Sal: 0,9/100g
+
+Salsa guacamole Old El Paso
+Sal: 0,8/100g
+
+### Especias
+
+Aunque en general no he encontrado especias que compensen comer sin sal, el curry es de lo que se acerca más.
diff --git a/blog/content/notas/index.gmi b/blog/content/notas/index.gmi
index 91763d3e..c3345f38 100644
--- a/blog/content/notas/index.gmi
+++ b/blog/content/notas/index.gmi
@@ -1,5 +1,7 @@
# Notas
+=> comprar-bajo-en-sodio Comprar bajo en sodio
+
## Lenguaje
=> lenguaje/bugs Bugs en el español
@@ -26,3 +28,4 @@
=> tecnologia/quiero-instalar-linux Quiero instalar Linux
=> tecnologia/problemas Problemas
+=> tecnologia/mama-quiero-ser-programador Mamá, quiero ser programador
diff --git a/programming/mama_quiero_ser_programador.md b/blog/content/notas/tecnologia/mama-quiero-ser-programador.gmi
index 7fc8ded3..01388fe2 100644
--- a/programming/mama_quiero_ser_programador.md
+++ b/blog/content/notas/tecnologia/mama-quiero-ser-programador.gmi
@@ -1,8 +1,6 @@
# Mamá, quiero ser programador
-Nuestro primer ordenador llego a casa cuando yo tenía cuatro años.
-A mi madre le gusta repetir que aquel día mi padre, mi hermano y yo no comimos.
-Desde entonces me han fascinado los ordenadores, lo que me ha llevado a la fascinación por programar.
+Nuestro primer ordenador llego a casa cuando yo tenía cuatro años. A mi madre le gusta repetir que aquel día mi padre, mi hermano y yo no comimos. Desde entonces me han fascinado los ordenadores, lo que me ha llevado a la fascinación por programar.
Curiosamente, estudié ingeniería informática un poco por casualidad e incluso cuando acabé la carrera, tenía mis dudas de si sería mi carrera profesional.
@@ -14,26 +12,19 @@ Este texto intenta recoger mis opiniones sobre estos temas.
## Observaciones sobre el mercado laboral
-Los trabajos de programador parecen reflejar que hay mucho trabajo y poca gente capacitada para hacerlo.
-Hay bastantes trabajos comparativamente bien pagados y con buenas condiciones.
+Los trabajos de programador parecen reflejar que hay mucho trabajo y poca gente capacitada para hacerlo. Hay bastantes trabajos comparativamente bien pagados y con buenas condiciones.
-Sin embargo, esto mayormente aplica a los trabajadores con bastante experiencia.
-Gente con poca experiencia comenta que encontrar un trabajo requiere un esfuerzo desproporcionado, si se consigue encontrar siquiera trabajo.
+Sin embargo, esto mayormente aplica a los trabajadores con bastante experiencia. Gente con poca experiencia comenta que encontrar un trabajo requiere un esfuerzo desproporcionado, si se consigue encontrar siquiera trabajo.
Propongo que esto se debe a que la mayoría de ofertas son para programadores con experiencia y el atractivo de la profesión ha generado un número de candidatos mucho mayor que las ofertas disponibles, creando la situación inversa que la de los programadores con experiencia.
-Además, la profesión de programador tiene la peculiaridad de que muchos profesionales dedican mucho de su tiempo libre a ejercer, más allá del trabajo o los estudios.
-Como el proceso de obtener un trabajo tiene elementos de competencia con el resto de candidatos a un puesto, entre programadores con poca experiencia se ha popularizado autoformarse para mejorar sus posibilidades.
-Esta medida sólo es efectiva cuando nos permite destacar sobre otros competidores, con lo que cada vez parece necesitarse más esfuerzo de autoformación para competir.
+Además, la profesión de programador tiene la peculiaridad de que muchos profesionales dedican mucho de su tiempo libre a ejercer, más allá del trabajo o los estudios. Como el proceso de obtener un trabajo tiene elementos de competencia con el resto de candidatos a un puesto, entre programadores con poca experiencia se ha popularizado autoformarse para mejorar sus posibilidades. Esta medida sólo es efectiva cuando nos permite destacar sobre otros competidores, con lo que cada vez parece necesitarse más esfuerzo de autoformación para competir.
### La irrupción de los LLMs y la crisis postpandemia
-En 2020, diversos factores generaron un crecimiento del sector mayor de lo habitual.
-Sin embargo, a partir de mediados de 2022, se dispararon los despidos en el sector.
-<https://layoffs.fyi> recoge cifras de despidos que desde el segundo trimestre de 2022 siempre se han mantenido como mínimo bastante por encima del periodo 2020-2021, con un pico de despidos en el tercer trimestre de 2023.
+En 2020, diversos factores generaron un crecimiento del sector mayor de lo habitual. Sin embargo, a partir de mediados de 2022, se dispararon los despidos en el sector. layoffs.fyi recoge cifras de despidos que desde el segundo trimestre de 2022 siempre se han mantenido como mínimo bastante por encima del periodo 2020-2021, con un pico de despidos en el tercer trimestre de 2023.
-Además, en noviembre de 2022, OpenAI lanzó ChatGPT.
-Desde entonces, muchos han augurado que los LLMs pueden afectar significativamente al mercado laboral en general y al sector en particular.
+Además, en noviembre de 2022, OpenAI lanzó ChatGPT. Desde entonces, muchos han augurado que los LLMs pueden afectar significativamente al mercado laboral en general y al sector en particular.
Finalmente, muchos interpretan movimientos políticos, económicos y otras inestabilidades como otra crisis mundial en ciernes.
@@ -51,42 +42,33 @@ No hay certezas para predecir el futuro, pero podemos observar el pasado.
Apostar por la programación antes parece una buena idea a posteriori, pero mucha gente ha abandonado el sector y no todo el mundo ha tenido trabajos buenos y bien pagados.
-[Face it: you're a crazy person](https://www.experimental-history.com/p/face-it-youre-a-crazy-person) es un artículo que propone que escoger una profesión debería basarse en lo atractivo que nos resultan *todas* las partes del trabajo, sobre todo las peores.
+=> https://www.experimental-history.com/p/face-it-youre-a-crazy-person Face it: you're a crazy person es un artículo que propone que escoger una profesión debería basarse en lo atractivo que nos resultan *todas* las partes del trabajo, sobre todo las peores.
Ya cuando estudiaba, mucha gente se imaginaba divirtiéndose programando videojuegos.
-En mi opinión, algunas de las peores partes de la programación son las prisas; siempre se hace todo con menos tiempo del que querríamos.
-Eso influye en que lo que hacemos y lo que usamos suele estar mal documentado o no funciona bien, haciendo que la programación sea menos "construir cosas que sirven de algo" y más "reparar con mil chapuzas cosas ligeramente estropeadas".
+En mi opinión, algunas de las peores partes de la programación son las prisas; siempre se hace todo con menos tiempo del que querríamos. Eso influye en que lo que hacemos y lo que usamos suele estar mal documentado o no funciona bien, haciendo que la programación sea menos "construir cosas que sirven de algo" y más "reparar con mil chapuzas cosas ligeramente estropeadas".
-Además, lo otro es que muy probablemente tendremos que dedicar tiempo no remunerado y fuera de nuestra formación en formarnos, en general haciendo cosas que si bien pueden resultar más gratificantes, en general también serán frustrantes.
-(Además, de cara a conseguir trabajo, lamentablemente en general también ayudará muchísimo *completar* cosas que podamos poner en nuestro currículum.)
+Además, lo otro es que muy probablemente tendremos que dedicar tiempo no remunerado y fuera de nuestra formación en formarnos, en general haciendo cosas que si bien pueden resultar más gratificantes, en general también serán frustrantes. (Además, de cara a conseguir trabajo, lamentablemente en general también ayudará muchísimo *completar* cosas que podamos poner en nuestro currículum.)
-A otro nivel, los trabajos de programación que pudieran resultar más motivadores y edificantes son por general los peor pagados y con peores condiciones, mientras que los buenos suelen ser en general los que despertarán menos vocación en nadie.
-En mi opinión, es complicado conseguir algo de realización en este sector sin sacrificar la mayoría de beneficios que muchos ven en la profesión.
+A otro nivel, los trabajos de programación que pudieran resultar más motivadores y edificantes son por general los peor pagados y con peores condiciones, mientras que los buenos suelen ser en general los que despertarán menos vocación en nadie. En mi opinión, es complicado conseguir algo de realización en este sector sin sacrificar la mayoría de beneficios que muchos ven en la profesión.
-A corto plazo, mi previsión es que todo esto empeore.
-El sector muy probablemente seguirá siendo una opción mucho mejor que la mayoría, pero creo que las expectativas laborales deberán rebajarse.
-El único consejo que se me ocurre es intentar construir cosas similares a las que vemos en el mundo real para ver si nos gusta realmente el trabajo.
+A corto plazo, mi previsión es que todo esto empeore. El sector muy probablemente seguirá siendo una opción mucho mejor que la mayoría, pero creo que las expectativas laborales deberán rebajarse. El único consejo que se me ocurre es intentar construir cosas similares a las que vemos en el mundo real para ver si nos gusta realmente el trabajo.
## Consiguiendo un trabajo
Los procesos de contratación son una parte proporcionalmente muy pequeña de la vida laboral pero que concentran gran parte de lo que se habla y se protesta en este sector.
-Tengo más de dos décadas de experiencia profesional, creo que he tenido muy buenos trabajos y generalmente voy a puestos con menos competencia de lo normal.
-Pero para encontrar trabajo, en ocasiones he tenido que presentarme a más de un centenar de ofertas y llevarme innumerables rechazos de todo tipo, silenciosos y sonoros.
+Tengo más de dos décadas de experiencia profesional, creo que he tenido muy buenos trabajos y generalmente voy a puestos con menos competencia de lo normal. Pero para encontrar trabajo, en ocasiones he tenido que presentarme a más de un centenar de ofertas y llevarme innumerables rechazos de todo tipo, silenciosos y sonoros.
-Hay estudios que parecen demostrar que una parte muy importante de ofertas en el sector incluso son totalmente ficticias.
-(Esto seguramente afecte a otros sectores, pero parece especialmente popular en este.)
+Hay estudios que parecen demostrar que una parte muy importante de ofertas en el sector incluso son totalmente ficticias. (Esto seguramente afecte a otros sectores, pero parece especialmente popular en este.)
Los procesos de selección de personal tienen una gran parte de competencia porque en general, siempre hay otros candidatos que se esfuerzan como nosotros en ser los elegidos.
### Fuentes de ofertas
-Aunque creo que las grandes plataformas de empleo son menos efectivas que otras vías para encontrar trabajo, sí vale la pena examinar las ofertas para saber qué demanda el mercado y de paso apuntarse a todas las ofertas que podamos.
-Esto último igual hasta nos sirve para entrar en algún proceso y quizá conseguir trabajo, pero también es importante porque los procesos de selección requieren práctica real para mejorar nuestras posibilidades.
+Aunque creo que las grandes plataformas de empleo son menos efectivas que otras vías para encontrar trabajo, sí vale la pena examinar las ofertas para saber qué demanda el mercado y de paso apuntarse a todas las ofertas que podamos. Esto último igual hasta nos sirve para entrar en algún proceso y quizá conseguir trabajo, pero también es importante porque los procesos de selección requieren práctica real para mejorar nuestras posibilidades.
-Es importante recordar que muy frecuentemente lo que parecen requisitos en estas ofertas de empleo no lo son.
-Si una empresa pide más conocimientos en una oferta de lo que es razonable, es muy probable que no encuentren a nadie que los cumpla todos y que contraten a alguien que no cumple todos los requisitos.
+Es importante recordar que muy frecuentemente lo que parecen requisitos en estas ofertas de empleo no lo son. Si una empresa pide más conocimientos en una oferta de lo que es razonable, es muy probable que no encuentren a nadie que los cumpla todos y que contraten a alguien que no cumple todos los requisitos.
En general, el mejor lugar para encontrar mejores vacantes son las pequeñas comunidades:
@@ -95,12 +77,9 @@ En general, el mejor lugar para encontrar mejores vacantes son las pequeñas com
* Así mismo, muchas tecnologías también tienen sus propias comunidades, aunque haya menos específicamente españolas.
(En general, conseguir trabajos en el extranjero es bastante más complicado, así que recomiendo centrarse en comunidades españolas.)
-Muchas de estas comunidades tienen tablones de anuncios de ofertas de empleo.
-Muchas de estas ofertas las ponen los miembros de la comunidad y no las empresas, con lo que es más probable que sean reales, y en muchos casos, podremos hablar con la persona que pone el anuncio directamente.
-Además, en muchos casos los tablones de anuncios de comunidades tienen reglas más estrictas sobre publicación de rangos salariales y claridad en condiciones (como por ejemplo, la modalidad real de remoto).
+Muchas de estas comunidades tienen tablones de anuncios de ofertas de empleo. Muchas de estas ofertas las ponen los miembros de la comunidad y no las empresas, con lo que es más probable que sean reales, y en muchos casos, podremos hablar con la persona que pone el anuncio directamente. Además, en muchos casos los tablones de anuncios de comunidades tienen reglas más estrictas sobre publicación de rangos salariales y claridad en condiciones (como por ejemplo, la modalidad real de remoto).
-El volumen es por supuesto muy inferior, pero merece mucho la pena encontrar cuantos más tablones de anuncios de este tipo y centrarse más en sus ofertas.
-(Aunque raramente tendremos suficiente con estas ofertas para encontrar empleo, con lo que siempre deberemos tirar de las grandes plataformas.)
+El volumen es por supuesto muy inferior, pero merece mucho la pena encontrar cuantos más tablones de anuncios de este tipo y centrarse más en sus ofertas. (Aunque raramente tendremos suficiente con estas ofertas para encontrar empleo, con lo que siempre deberemos tirar de las grandes plataformas.)
### El currículum y la presencia online
@@ -118,14 +97,13 @@ Un proceso de selección jamás puede evaluar adecuadamente la capacidad de un c
Al no ser algo muy exacto, los procesos de selección tienen muchísimo de imitación y modas.
-Esto tiene una ventaja; en cualquier momento determinado de tiempo hay como media docena de tipos de entrevista.
-Además, para cada tipo de prueba de moda, hay bastantes materiales para preparar la prueba.
+Esto tiene una ventaja; en cualquier momento determinado de tiempo hay como media docena de tipos de entrevista. Además, para cada tipo de prueba de moda, hay bastantes materiales para preparar la prueba.
En mi opinión, hay que aceptar los sinsentidos de los procesos de selección, dedicar una cantidad significativa de tiempo para prepararnos los pocos formatos más populares en ese momento, y quizá consolarnos con que el número de formatos populares no es mucho mayor.
### Escogiendo empleos
-[La falacia de McNamara dice que a la hora de tomar decisiones damos más importancia a lo que es fácil de medir con un número que a lo que no](https://es.wikipedia.org/wiki/Falacia_de_McNamara).
+=> https://es.wikipedia.org/wiki/Falacia_de_McNamara La falacia de McNamara dice que a la hora de tomar decisiones damos más importancia a lo que es fácil de medir con un número que a lo que no.
El sueldo es de las pocas variables que podemos conocer cuando tenemos una oferta en la mesa, pero no es tan mala métrica.
diff --git a/blog/content/notes/cliffs/the-tyranny-of-structurelessness.gmi b/blog/content/notes/cliffs/the-tyranny-of-structurelessness.gmi
new file mode 100644
index 00000000..3dfc17ea
--- /dev/null
+++ b/blog/content/notes/cliffs/the-tyranny-of-structurelessness.gmi
@@ -0,0 +1,98 @@
+# The tyranny of structurelessness
+
+=> https://www.jofreeman.com/joreen/tyranny.htm A copy of the original article. These Cliff's notes focus on the generic points of the article, not on its original context (the feminist movement).
+
+Leaderless, structureless groups as an organizational form is a reaction to over-structured society in which most of us live that give others control over us.
+
+Structurelessness encourages participation in discussion and personal insight, but it does not achieve more than that.
+
+Structureless groups struggle when they want to achieve something more specific than raising consciousness, because the groups do not want to change structure when they change their tasks, because they think other organizational forms can be anything but oppressive.
+
+## Formal and informal structures
+
+Structureless groups evolve into having tacit structure due to the diversity of the people that form them.
+
+Structurelessness only prevents the formation of formal structures, not informal ones. Decision-making rules are known only by those who make the decisions.
+
+To give everyone the opportunity to participate, structure must be explicit and the rules of decision-making must be open and available to everyone, so they must be formalized.
+
+## The nature of elitism
+
+Elites can only be groups, not individuals.
+
+Elites have power over a larger group without direct responsibility.
+
+A person is an elitist by being a part or advocating the elite, not by being notorious.
+
+Elites are not conspiracies, generally they are groups of friends that happen to participate in some activity together.
+
+Elites are communication networks because they are groups of friends that talk.
+
+Groups might have one or more communication networks and they might overlap. The communication networks do not necessarily have to be an elite. Multiple communication groups might compete and only one might become an elite.
+
+In a structured group, the group competition is public and other members of the group can arbitrate and make demands on the groups.
+
+Elites can be spotted in groups, they listen and don't interrupt other members more than they do with non-members. Approval of the elite is necessary for things to happen.
+
+Membership of the elite tends to have some required characteristic. Common themes are related to the friendship nature of the elite, but not to the effectiveness for the larger group's purpose.
+
+It is easier to form an elite at the beginning of the group, by bringing existing friends in. Otherwise, the elite must be formed through new effort. Elites need to maintain themselves by adding new members. Outsides might find a member of the elite to sponsor them.
+
+Elite forming and maintenance require time, so people with major commitments normally find it impossible to join. A formal structure of decision making helps the overworked (and others) participate in the group.
+
+Elites are not inevitably bad, they are only inevitable. Elites can do useful things. But elites have uncontrolled power within their group.
+
+Two negative consequences: liked people have power independently of their skills, which is bad for doing significant things; elites have no obligation to be responsible. The elite usually tries to be responsible to maintain their influence, but the group cannot compel them to be responsible, this is up to the interests of the elite.
+
+## The "star" system
+
+Society expects groups to make decisions and to select spokespeople. Society does not want to listen to all individuals in a group, they want to know what the group feels. There are only three ways to know group opinion:
+
+* Voting
+* Surveys
+* Spokespeople
+
+The public is conditioned to look for spokespeople.
+
+If there are no official spokespeople, the public might choose notable members of the group, but their opinions might not be representative of the group: "stars".
+
+The stars might not desire to be, and the members of the group might resent the stars.
+
+Stars cannot be removed by the group, because the group did not make them stars, only the press can. The press will listen to stars as long as there are no official spokespeople. Members of the group can attack the stars, who might then leave the group, remaining a star but maybe not aligned with the group.
+
+## Political impotence
+
+Sometimes the informal structure of a group might align with what the group wants to do, this gives the appearance of an effective group. However, this is hard to replicate. Normally these groups have four conditions:
+
+* They are task oriented, they were formed with a narrow and specific function.
+* They are small and homogeneous, so they have good communication reducing conflicts.
+* They have a high degree of communication. This normally limits the group to five people, although 10 to 15 is possible if they have subgroups.
+* They have a low degree of skill specialization, so everything can be done by more than one person, so no one is indispensable. (Not everything needs to be doable by everyone.)
+
+Groups composed of smaller effective groups do not tend to become more effective than their parts. These groups generates much motion and few results. These groups tend to be limited to the initial founders and exclude others, esp. the nongregarious, and elitism becomes institutionalized.
+
+Groups without projects spend their time maintaining the elite.
+
+When people cannot join the group and do things, they might do things on their own, which might lead to individual creativity, but many people cannot do this and does not foster cooperative group effort. Such people might drop out of the interests of the group, or join groups with other interests, maybe with new elites.
+
+The old elites can perceive these new elites as misaligned threats. The old elites can accuse the new elite of attracting specific groups of people from their group.
+
+The old elites can become public and formalize their original power structure as a formal structure. If the informal elite was well structured they might be able to do this, but groups that required structure the most might not be able to do it, because they adhere more to the ideology of structurelessness and they are more vulnerable to a takeover.
+
+Unstructured groups might choose to participate in larger groups with more influence and capabilities, but they can only have little influence in the larger group and their ideas might be diffused, but rarely implemented.
+
+## Principles of democratic structuring
+
+Groups should not blindly accept or ignore traditional forms of organization. These forms might be effective or not. Structure is not inherently bad, only excess of it.
+
+Essential principles:
+
+* Democratic delegation of specific authority to specific individuals. People who show interest or willingness who are selected are committed.
+* Delegates should be responsible to who selected them. This way the group controls the authorities.
+* Distribution of authority over as many people as possible, preventing monopoly of power and requiring consultations. It gives more people the opportunity for responsibility and learning.
+* Rotation of tasks among individuals to prevent responsibilities from being someone's property. But not too much rotation so that it prevents learning and satisfaction.
+* Rational allocation of tasks; by ability, interest and responsibility instead of by standing in the group. Learn through apprenticeship rather than sink or swim. Do not demoralize people by having responsibilities you cannot do well. Do not blacklist people from doing what they can do well.
+* Diffusion of information to everyone as frequently as possible, giving individuals more power.
+* Equal access to resources needed by the group. Resources owned by a member can be controlled by a member. This includes skills and information.
+
+These principles prevent informal elites.
diff --git a/blog/content/notes/index.gmi b/blog/content/notes/index.gmi
index 31188065..7f8f12ba 100644
--- a/blog/content/notes/index.gmi
+++ b/blog/content/notes/index.gmi
@@ -1,15 +1,16 @@
# Notes
+=> interesting-articles Interesting articles
=> greek-task-list Greek task list
## Cliff's notes
-Notes about some books I like:
+Notes about some books and long articles I like:
=> cliffs/mythical-man-month The Mythical Man-Month
=> cliffs/governable-spaces Governable Spaces
=> cliffs/peopleware Peopleware
-
+=> cliffs/the-tyranny-of-structurelessness The tyranny of structurelessness
## Tech
=> tech/misc-linux-stuff Misc Linux stuff
@@ -20,6 +21,20 @@ Notes about some books I like:
=> tech/ledger Ledger
=> tech/migadu Migadu
=> tech/ripping Ripping
+=> tech/about-apis About APIs
+=> tech/about-relational-databases About relational databases
+=> tech/containers-might-not-be-the-right-answer Containers might not be the right answer
+=> tech/crud-is-an-important-unsolved-problem CRUD is an important unsolved problem
+=> tech/about-django About Django
+=> tech/git-advice Git advice
+=> tech/github-annoyances GitHub annoyances
+=> tech/prolog-vs-sql Prolog vs. SQL
+=> tech/take-the-less-traveled-road Take the less traveled road
+=> tech/the-tragedy-of-the-geeks The tragedy of the geeks
+=> tech/misc-python-stuff Misc Python stuff
+=> tech/python-modules-primer Python modules primer
+=> tech/so-you-want-to-play-with-functional-programming So you want to play with functional programming
+=> tech/motivating-example-for-logical-replication-for-dynamic-ui Motivating example for logical replication for dynamic UI
### Gadgets
diff --git a/blog/content/notes/interesting-articles.gmi b/blog/content/notes/interesting-articles.gmi
new file mode 100644
index 00000000..fbf0af04
--- /dev/null
+++ b/blog/content/notes/interesting-articles.gmi
@@ -0,0 +1,192 @@
+# Interesting articles
+
+## General
+
+=> https://lukeplant.me.uk/blog/posts/no-one-actually-wants-simplicity/ No one actually wants simplicity
+Simplicity is sacrifice. See also:
+=> https://www.youtube.com/watch?v=SxdOUGdseq4 simple made easy (video)
+=> https://www.seangoedecke.com/wicked-features/ wicked features
+
+=> https://www.geoffreylitt.com/2025/03/03/the-nightmare-bicycle.html Avoid the nightmare bicycle
+Good designs expose systematic structure; they lean on their users’ ability to understand this structure and apply it to new situations.
+
+## Programming
+
+=> https://mikehadlow.blogspot.com/2012/05/configuration-complexity-clock.html The Configuration Complexity Clock
+Programming languages, configuration files, DSLs for configuration
+
+=> https://olano.dev/2023-11-30-code-is-run-more-than-read/ Code is run more than read
+A unified theory of broken software
+
+=> https://www.teamten.com/lawrence/writings/java-for-everything.html Java for Everything
+The advantages of focusing on a single language and how performance and static typing are helpful.
+
+=> https://en.wikipedia.org/wiki/Ostrich_algorithm Ostrich algorithm
+
+=> https://blog.brownplt.org/2024/04/12/behavior-misconceptions.html Finding and Fixing Standard Misconceptions About Program Behavior
+About the Standard Model of Languages (SMoL)
+
+=> https://dannorth.net/best-simple-system-for-now/ Best Simple System for Now
+A view I disagree on about IAGNI and the opposite concepts, but interesting
+
+=> https://mmapped.blog/posts/38-static-types-perfectionism Static types are for perfectionists
+Our programming style is influenced by our personality and life
+
+=> https://www.joelonsoftware.com/2005/05/11/making-wrong-code-look-wrong/ Making wrong code look wrong
+The history about Hungarian notations
+
+=> https://www.hillelwayne.com/talks/ese/ddd/ What We Know We Don't Know: Empirical Software Engineering
+40-minute video about the power of proper sleep, working schedules and stress levels vs. engineering practices
+
+=> https://www.hillelwayne.com/post/we-are-not-special/ We are not special
+Second of a series of three articles comparing software engineering with traditional engineering. Mostly dispels some myth and lack of knowledge about traditional engineering.
+
+### Testing
+
+=> https://testing.googleblog.com/2014/05/testing-on-toilet-risk-driven-testing.html Testing on the Toilet: Risk-Driven Testing
+"Your tests are a means. The bang is what counts. It’s your job to maximize it."
+
+=> https://testing.googleblog.com/2024/10/smurf-beyond-test-pyramid.html SMURF: Beyond the Test Pyramid
+Test categories and the pyramid are excessively limited models.
+
+### Python
+
+=> https://lukeplant.me.uk/blog/posts/pythons-disappointing-superpowers/ Python’s "Disappointing" Superpowers
+A convincing defense of dynamic typing
+
+### Rust
+
+=> https://www.hezmatt.org/~mpalmer/blog/2024/05/01/the-mediocre-programmers-guide-to-rust.html The Mediocre Programmer's Guide to Rust
+=> https://qouteall.fun/qouteall-blog/2025/How%20to%20Avoid%20Fighting%20Rust%20Borrow%20Checker How to Avoid Fighting Rust Borrow Checker
+
+### Optimization
+
+=> https://docs.oracle.com/cd/E11882_01/server.112/e41573/technique.htm The Oracle Performance Improvement Method
+My favorite text about performance tuning- the good advice is not Oracle-specific. Includes a bit more real-world advice than:
+=> https://users.ece.utexas.edu/~adnan/pike.html Rob Pike's 5 Rules of Programming
+
+=> https://infrequently.org/series/performance-inequality/ The Performance Inequality Gap, 2024
+=> https://danluu.com/slow-device/ How web bloat impacts users with slow devices
+About janky browser applications and websites.
+
+### Git
+
+=> https://blog.gitbutler.com/git-tips-3-really-large-repositories/ Git Tips 3: Really Large Repositories
+
+### Accessibility
+
+=> https://xogium.me/the-text-mode-lie-why-modern-tuis-are-a-nightmare-for-accessibility The text mode lie: why modern TUIs are a nightmare for accessibility
+
+## Systems
+
+=> https://chrisdown.name/2018/01/02/in-defence-of-swap.html In defence of swap: common misconceptions
+
+## Organizations
+
+=> [[https://charity.wtf/2024/07/24/pragmatism-neutrality-and-leadership/ Pragmatism, Neutrality and Leadership
+(The parts about "As a leader, your job is to succeed", "Companies with shitty cultures win all the time".) This article connects with:
+=> https://hbr.org/2007/03/why-i-wrote-the-no-asshole-rule The no asshole rule book
+
+=> https://charity.wtf/2017/05/11/the-engineer-manager-pendulum/ The Engineer/Manager Pendulum
+Why people should multiclass engineering and management
+
+=> https://varoa.net/2024/01/09/how-organisations-cripple-engineering-teams-with-good-intentions.html How organisations cripple engineering teams with good intentions
+Arguments for having coders code
+
+=> https://stackoverflow.blog/2024/06/10/generative-ai-is-not-going-to-build-your-engineering-team-for-you/ Generative AI Is Not Going To Build Your Engineering Team For You
+Bad title; it's about the need for junior coders
+
+=> https://luminousmen.com/post/senior-engineer-fatigue Senior Engineer Fatigue
+
+=> https://www.joelonsoftware.com/2000/04/06/things-you-should-never-do-part-i/ Things You Should Never Do, Part I
+About rewriting software from scratch
+
+=> https://dl.acm.org/doi/10.1145/1464122.1464146 Some observations concerning large programming efforts
+Someone figured most of it out in 1964.
+
+=> https://www.jofreeman.com/joreen/tyranny.htm The tyranny of structurelessness
+=> cliffs/the-tyranny-of-structurelessness (My Cliff's Notes)
+
+### Project management
+
+=> https://apenwarr.ca/log/20171213 An epic treatise on scheduling, bug tracking, and triage
+No non-sense opinions on project management I mostly agree with
+
+## News
+
+=> https://www.currentaffairs.org/2020/08/the-truth-is-paywalled-but-the-lies-are-free/ The Truth Is Paywalled But The Lies Are Free
+Excellent title, but the article is so-so
+
+## Society
+
+=> https://locadeldesvan.com/2025/01/09/contra-la-tecnocratizacion-de-la-vida/ Contra la tecnocratización de la vida
+About the pressure of the modern age and the privilege of being mediocre
+
+=> https://www.experimental-history.com/p/face-it-youre-a-crazy-person Face it: you're a crazy person
+Choosing a job because you like the worst parts of it
+
+## Epistemology?
+
+=> https://hermiene.net/essays-trans/relativity_of_wrong.html The Relativity of Wrong by Isaac Asimov
+All physics theories are strictly "false", but they are very true.
+
+## Meta
+
+=> https://www.benkuhn.net/progessays/ Essays on programming I think about a lot
+=> https://www.piglei.com/articles/en-programmer-reading-list-part-one/ A Programmer's Reading List: 100 Articles I Enjoyed (1-50)
+
+## Infrequent but useful terms
+
+=> https://en.wikipedia.org/wiki/Abilene_paradox The Abilene paradox
+A collective fallacy, in which a group of people collectively decide on a course of action that is counter to the preferences of most or all individuals in the group, while each individual believes it to be aligned with the preferences of most of the others.
+
+=> https://en.wikipedia.org/wiki/Dunning%E2%80%93Kruger_effect The Dunning–Kruger effect
+A cognitive bias in which people with limited competence in a particular domain overestimate their abilities. Some researchers also include the opposite effect for high performers: their tendency to underestimate their skills. In popular culture, the Dunning–Kruger effect is often misunderstood as a claim about general overconfidence of people with low intelligence instead of specific overconfidence of people unskilled at a particular task.
+=> https://www.frontiersin.org/journals/psychology/articles/10.3389/fpsyg.2022.840180/full A Statistical Explanation of the Dunning–Kruger Effect
+This effect might only be caused by subjects in the bottom quartile can only make optimistic errors placing themselves into a higher quartile, while subjects in the top quartile can only make pessimistic errors placing themselves in a lower quartile.
+
+=> https://en.wikipedia.org/wiki/Gell-Mann_amnesia_effect The Gell-Mann amnesia effect
+A cognitive bias describing the tendency of individuals to critically assess media reports in a domain they are knowledgeable about, yet continue to trust reporting in other areas despite recognizing similar potential inaccuracies.
+
+=> https://en.wikipedia.org/wiki/Goodhart%27s_law Goodhart's law
+An adage that has been stated as, "When a measure becomes a target, it ceases to be a good measure".
+
+=> https://en.wikipedia.org/wiki/McNamara_fallacy The McNamara fallacy
+(Also known as the quantitative fallacy) involves making a decision based solely on quantitative observations (or metrics) and ignoring all others.
+
+=> https://en.wikipedia.org/wiki/Hanlon%27s_razor Hanlon's razor
+An adage, or rule of thumb, that states: Never attribute to malice that which is adequately explained by stupidity.
+
+=> https://en.wikipedia.org/wiki/Hawthorne_effect The Hawthorne effect
+A type of human behavior reactivity in which individuals modify an aspect of their behavior in response to their awareness of being observed.
+
+=> https://en.wikipedia.org/wiki/Novelty_effect Novelty effect
+An effect of introducing new elements on some activity or behavior.
+
+=> https://softwareengineering.stackexchange.com/questions/123627/what-are-the-london-and-chicago-schools-of-tdd What are the London and Chicago schools of TDD?
+
+=> https://en.wikipedia.org/wiki/Sturgeon%27s_law Sturgeon's law
+An adage stating "ninety percent of everything is crap".
+
+=> https://en.wikipedia.org/wiki/Schedule_chicken Schedule chicken
+When two or more parties working towards a common goal all claim to be holding to their original schedules for delivering their part of the work, even after they know those schedules are impossible to meet. Each party hopes the other will be the first to have their failure exposed.
+
+=> https://everything2.com/title/Your+radical+ideas+about+society%252C+individualism%252C+and+religion+have+already+occurred+to+others Your radical ideas about society, individualism, and religion have already occurred to others
+
+=> https://en.wikipedia.org/wiki/Slate_Star_Codex#Lizardman's_Constant Lizardman's constant
+The approximate percentage of responses to a poll, survey, or quiz that are not sincere
+
+See also:
+
+=> greek-task-list Greek task list
+
+Sources:
+
+=> https://en.wikipedia.org/wiki/List_of_paradoxes List of paradoxes
+=> https://en.wikipedia.org/wiki/Unintended_consequences Unintended consequences
+
+## Lost and not found
+
+Some articles I'd like to find here, but haven't been able to find again:
+
+* Enqueuing function calls vs. extending your domain model: This article discussed using traditional queues for handling some actions in your application vs. doing this "declaratively". For example, enqueue "send notification about x to user y" vs. "add column 'needs_x_notification to users table". If I remember correctly, the article contained some insightful arguments for the latter approach I had not thought of.
diff --git a/programming/about_apis.md b/blog/content/notes/tech/about-apis.gmi
index 2c44f41e..f1732cc9 100644
--- a/programming/about_apis.md
+++ b/blog/content/notes/tech/about-apis.gmi
@@ -1,6 +1,6 @@
# About APIs
-The [Jeff Bezos' API memo](https://gist.github.com/kislayverma/d48b84db1ac5d737715e8319bd4dd368) is one of the most talked stories about API programming.
+=> https://gist.github.com/kislayverma/d48b84db1ac5d737715e8319bd4dd368 The Jeff Bezos' API memo is one of the most talked stories about API programming.
It is, in my opinion, also one of those things which are successful for some environments, but not for all.
@@ -8,24 +8,19 @@ It is, in my opinion, also one of those things which are successful for some env
An "operation" in your application can be in one of the following levels of "API accessibility":
-* -oo The operation cannot be invoked in isolation easily.
-For instance, it is embedded in an MVC controller, mixed with form handling and HTML generation, and thus the best approach to invoke it programatically is to simulate a browser
+* -oo The operation cannot be invoked in isolation easily. For instance, it is embedded in an MVC controller, mixed with form handling and HTML generation, and thus the best approach to invoke it programatically is to simulate a browser
* 0 The operation can be invoked, in-process, by calling a function or method, but requiring complex setup or using complex types (e.g. others than lists, maps, numbers and strings)
* 1 The operation can be invoked, in-process, by calling a function without complex setup and using plain types
* 2 The operation can be invoked, off-process, by calling a function without complex setup and using plain types
* 3 The operation can be invoked via a command line tool
* 4 The operation can be invoked via a network call
-Many proponents of APIs propose level 4 as the target.
-This obviously allows your operations to be integrated in separate processes via network calls, which is the most powerful way of API access.
-They will also reason that this will force your application to have a clean architecture with separation of concerns.
+Many proponents of APIs propose level 4 as the target. This obviously allows your operations to be integrated in separate processes via network calls, which is the most powerful way of API access. They will also reason that this will force your application to have a clean architecture with separation of concerns.
Note also that doing proper testing will probably force your operations to be tested to be in levels 0-2, as otherwise it will be annoyingly complex to test them.
-We propose that the architecture benefits of level 4 are also present in levels 0-3, but achieving these levels requires much less effort than achieving level 4 (where you need to add a network protocol, handle aspects such as authentication/authorization, marshalling/unmarshalling, etc.), so unless you require level 4, you can stay in levels 0-3.
-Going to level 3 instead of 0 should be easy when creating new operations, so that's the level of API accessibility we recommend new code to adhere to by default.
+We propose that the architecture benefits of level 4 are also present in levels 0-3, but achieving these levels requires much less effort than achieving level 4 (where you need to add a network protocol, handle aspects such as authentication/authorization, marshalling/unmarshalling, etc.), so unless you require level 4, you can stay in levels 0-3. Going to level 3 instead of 0 should be easy when creating new operations, so that's the level of API accessibility we recommend new code to adhere to by default.
Note also that level 3 can provide many benefits of level 4, but with less development overhead, so it's a level we recommend considering explicitly, as it is often overlooked.
-Level -oo is typical of legacy applications.
-Note that we consider the distance between level -oo and the rest of levels much bigger than the distance between the rest of levels.
+Level -oo is typical of legacy applications. Note that we consider the distance between level -oo and the rest of levels much bigger than the distance between the rest of levels.
diff --git a/blog/content/notes/tech/about-django.gmi b/blog/content/notes/tech/about-django.gmi
new file mode 100644
index 00000000..15f73600
--- /dev/null
+++ b/blog/content/notes/tech/about-django.gmi
@@ -0,0 +1,115 @@
+# About Django
+
+Without more context, one of the technologies I recommend to everyone is Django.
+
+Django is a Python web framework with "batteries included".
+
+Web frameworks can provide more or less tools to write applications. Typically, frameworks that provide fewer tools are more flexible and give developers more freedom to develop their applications in the best possible way. Similarly, frameworks that provide more tools tend to guide you towards a specific way to writing applications, and typically, require more work if you want to deviate.
+
+In my opinion, many applications you might need to develop are very similar and have similar issues, and solving them ad-hoc for each project is a waste. Therefore, I lean towards using frameworks that provide more batteries in most cases.
+
+(Certainly, there are projects that clearly need special approaches, or which deviate enough from any generic web framework.)
+
+In fact, most of the complaints described in this document are caused by Django having too few batteries, not too many!
+
+=> https://github.com/alexpdp7/django-tws Django training wheels is my project in alpha to address some of those shortcomings.
+
+## The Django admin
+
+Besides including more batteries than most other frameworks, and being in general a well-engineered framework in my opinion, Django includes the admin.
+
+The admin is a declarative way to build administrative sites where some users edit data stored in the application database.
+
+Many similar tools exist, but I have not found any other tool that can do so much.
+
+* The Django admin handles multi-table relationships very well, including picking foreign key targets and editing related table data. For example, if a person entity has a "parent" related foreign key relationship, the Django admin provides a search functionality to pick a person's parent. If the person entity has a list of children, the Django admin provides a way to add and edit children from the person form.
+* The Django admin has a simple, but useful for many scenarios permissions functionality, where editing certain entities is restricted to groups of users.
+
+The Django admin is frequently a big boost during the early development of database-backed applications, and sometimes it can provide value during a big part of the life of an application.
+
+Additionally, traditionally when working with frameworks without an equivalent facility, the friction of adding an interface to edit a piece of data can be large. Developers pressed for time might opt to hardcode the data in the source code of the application, requiring code changes to modify certain behaviors of the application. When the friction to add a user interface to edit such data is low, developers can configure the admin to let those users edit the data directly without going through the developers.
+
+## Django problems
+
+However, there are still many common issues for which batteries could exist, but that Django does not provide.
+
+### Django has no support or documentation about packaging Django projects
+
+Most Django projects have dependencies besides Django. In order to develop and deploy Django applications, you likely must install other dependencies. Django does not include documentation nor support to do this.
+
+Many different approaches and tools exist to manage Python project dependencies. Understandably, endorsing one particular approach in Django could be controversial.
+So Django leaves the choice of approach up to users. Additionally, Django adds a few difficulties in Python project management, and users must figure out how to handle Django projects in their chosen approach.
+
+Several initiatives have tried to tackle this problem, notably:
+
+=> https://github.com/radiac/nanodjango nanodjango
+
+### Django settings are a partial solution
+
+Django provides settings to manage the configuration for a Django project. You implement Django settings by writing a Python module.
+
+For example, the default Django template includes the following snippet to configure the database connection:
+
+```
+DATABASES = {
+ 'default': {
+ 'ENGINE': 'django.db.backends.sqlite3',
+ 'NAME': BASE_DIR / 'db.sqlite3',
+ }
+}
+```
+
+Besides assigning a setting directly like in the preceding snippet, you can use Python code to assign settings.
+
+This allows you to tackle many common issues, such as setting up a different database connection for development and production, while keeping the production database credentials away from the source code repository. There are many similar issues that you must tackle in nearly all projects.
+
+Several initiatives tackle some of those issues:
+
+=> https://github.com/jazzband/dj-database-url dj-database-url provides a way to configure the database connection through an environment variable.
+
+### Django does not explain a development database workflow
+
+Django provides migrations to handle schema changes. Migrations work well and are a valid solution to handle schema changes in production.
+
+However, while developing a Django application, you frequently need to make many temporary changes to the data definition until you find the right data definition.
+
+In my opinion, if you follow the Django documentation, then you might end up using migrations for those development schema changes. This is awkward and problematic, and there are procedures to develop database changes that work better.
+
+I would like a command that recreates your database, applying unmigrated model changes. This command could also have hooks to load sample data. (Likely, Python code and not fixtures.)
+
+### Django only tackles database-based, server-side-rendered, non highly interactive web applications
+
+While certainly a huge amount of applications:
+
+* Revolve around data stored in a relational database
+* Are better implemented as server-side rendering applications
+* Do not require very complex or real-time interactions
+
+There are certainly many applications that do not fit this mold.
+
+In my opinion, focusing on database-based applications is a good decision. Many Django features (like the admin) revolve around the database, and a framework oriented to other applications likely should be very different.
+
+However, more and more applications break the limits of server-side rendering, and while you can build such applications with Django, you need a lot of effort or finding additional libraries to use.
+
+For example:
+
+* Django REST framework provides a layer to provide REST APIs on top of the Django ORM.
+* Projects exist to add support for Django for front end frameworks such as htmx or Hotwire. These frameworks are an intermediate step between traditional server-side-rendered applications and JavaScript front ends, enabling most of the benefits of JavaScript front ends within the traditional server-side rendering approach.
+
+Additionally, providing an API is also useful beyond JavaScript front ends. APIs are necessary for other purposes, such as implementing mobile apps to interact with your application, or just providing an API for programmatic access to your application.
+
+### Some common tasks should have more tutorial content
+
+The Django documentation is mostly for reference, covering all Django features, but with little content on how to use Django. The items I list below likely are documented on books, websites, forums, etc. If you know a good source for many of those, even if it is paid, feel free to let me know to add references.
+
+* Restricting users in the admin to a subset of the instances of a model.
+
+For example, users belong to organizations and users should only see instances of some model related to their organization. The FAQ contains "How do I limit admin access so that objects can only be edited by the users who created them?", which is a very similar question and points to the features you need to use to achieve these goals. These requirements are often related to requiring "extending the existing User model".
+
+* Having a search UI for reference fields instead of dropdowns.
+
+Many projects similar to the admin only offer dropdowns for reference fields. This does not work when the referenced objects are more than a couple. Django calls this raw_id_fields, and it is difficult to learn that this feature exists.
+
+## Further reading
+
+=> crud-is-an-important-unsolved-problem CRUD is an important unsolved problem
diff --git a/programming/about_relational_databases.md b/blog/content/notes/tech/about-relational-databases.gmi
index 464de330..d08071ac 100644
--- a/programming/about_relational_databases.md
+++ b/blog/content/notes/tech/about-relational-databases.gmi
@@ -8,23 +8,20 @@ Actually, the relations in a relational database are the tables.
A relation "relates" a set of values with another set of values.
-For example, a relation can relate the name of a person with their birth date and birth place.
-For example:
+For example, a relation can relate the name of a person with their birth date and birth place. For example:
-```
(person name) => (birth date, birth place)
(Alice) => (1979-12-03, Barcelona)
(Bob) => (1995-03-04, Paris)
...
-```
Many computer languages have similar concepts:
-* [Python mapping types such as `dict`](https://docs.python.org/3/library/stdtypes.html#mapping-types-dict)
-* C++ `std::map`
-* Java `java.util.Map`
-* [C# `System.Collections.Generic.Dictionary`](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.dictionary-2?view=net-9.0)
-* [Javascript `Object`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)
-* [PHP arrays](https://www.php.net/manual/en/language.types.array.php)
+* Python mapping types such as dict
+* C++ std::map
+* Java java.util.Map
+* C# System.Collections.Generic.Dictionary
+* JavaScript Object
+* PHP arrays
Relations are a natural concept, so although non-relational data systems exist, most data can be stored as relations.
diff --git a/programming/containers_might_not_be_the_right_answer.md b/blog/content/notes/tech/containers-might-not-be-the-right-answer.gmi
index b00bbade..de614983 100644
--- a/programming/containers_might_not_be_the_right_answer.md
+++ b/blog/content/notes/tech/containers-might-not-be-the-right-answer.gmi
@@ -2,20 +2,17 @@
Containers are everywhere, and I feel today they are the default answer to many problems for many people.
-[Although the author of one of the handiest quotes does not want people to use it](https://www.jwz.org/blog/2014/05/so-this-happened/), I think that quote adequately describes the situation.
-Definitely, containers are no silver bullet.
+=> https://www.jwz.org/blog/2014/05/so-this-happened/ Although the author of one of the handiest quotes does not want people to use it, I think that quote adequately describes the situation. Definitely, containers are no silver bullet.
-Containers are a good example of an “easy but not simple” technique (see [the "Simple Made Easy" - Rich Hickey (2011) talk](https://www.youtube.com/watch?v=SxdOUGdseq4)).
+Containers are a good example of an “easy but not simple” technique.
-Containers are easy because they automate getting arbitrary "isolated" Linux environments and running processes on them.
-Additionally, you can find container images for mostly everything on the Internet.
-For this reason, it is very easy to run much software using containers, often with a single command.
+=> https://www.youtube.com/watch?v=SxdOUGdseq4 (See the "Simple Made Easy" - Rich Hickey 2011 talk.)
-However, containers are easy, but not simple.
-Containers combine many different techniques to achieve their ease, and thus, frequently you hit problems derived from any of the techniques containers use.
+Containers are easy because they automate getting arbitrary "isolated" Linux environments and running processes on them. Additionally, you can find container images for mostly everything on the Internet. For this reason, it is very easy to run much software using containers, often with a single command.
-However, Docker popularized many good ideas, both directly related to containerization and general ideas!
-There are still places where containers are the right answer.
+However, containers are easy, but not simple. Containers combine many different techniques to achieve their ease, and thus, frequently you hit problems derived from any of the techniques containers use.
+
+However, Docker popularized many good ideas, both directly related to containerization and general ideas! There are still places where containers are the right answer.
## Reasons not to use containers
@@ -35,11 +32,9 @@ Most issues can be worked around, but this requires more effort, or at least, mo
### Container images are big and expensive
-Optimizing the size of container images can require significant effort.
-Popular public images are often optimized for size, but even with optimized images, storing and moving container images frequently requires much more bandwidth and storage than the alternatives.
+Optimizing the size of container images can require significant effort. Popular public images are often optimized for size, but even with optimized images, storing and moving container images frequently requires much more bandwidth and storage than the alternatives.
-There are free ways to host private container images, but they are frequently limited in size, bandwidth, or both.
-You can easily run into Docker Hub limits, GitHub only provides 2gb of storage, etc.
+There are free ways to host private container images, but they are frequently limited in size, bandwidth, or both. You can easily run into Docker Hub limits, GitHub only provides 2gb of storage, etc.
Building container images also can require significant resources.
@@ -47,23 +42,17 @@ Building container images also can require significant resources.
Using containers frequently requires learning quite a few things specific about containers.
-* `Containerfile` design is not obvious.
- Some questions, like `ADD` and `COPY`, or `CMD` and `ENTRYPOINT` are difficult and not well documented.
+* Containerfile design is not obvious. Some questions, like ADD and COPY, or CMD and ENTRYPOINT are difficult and not well documented.
-* Container design is not obvious.
- Docker popularized "application containers", a fuzzy concept that is related to "single process containers", 12 factor architecture, and a few other ideas.
- Solving your problem might require good knowledge of application container design and use.
+* Container design is not obvious. Docker popularized "application containers", a fuzzy concept that is related to "single process containers", 12 factor architecture, and a few other ideas. Solving your problem might require good knowledge of application container design and use.
-* Container tools are complex, because containerization is difficult.
- Likely you need to know some intricate details of how Linux file permissions and users work, for example.
+* Container tools are complex, because containerization is difficult. Likely you need to know some intricate details of how Linux file permissions and users work, for example.
Not using containers can mean avoiding having to think about these things, and being able to use the time you save to actually solve your problem.
### Docker is not so good, targeting multiple container engines is not trivial
-Docker was the first popular container engine.
-Docker was a revolution and invented or popularized many great ideas.
-However, knowledge about containers was not well established when Docker was invented, and since then, better ways of doing many things have been discovered.
+Docker was the first popular container engine. Docker was a revolution and invented or popularized many great ideas. However, knowledge about containers was not well established when Docker was invented, and since then, better ways of doing many things have been discovered.
Other tools such as Podman or Fedora Toolbx and adjacent tools such as Distrobox have introduced many improvements respect Docker, while still reusing and being compatible with many Docker concepts.
@@ -71,8 +60,7 @@ However, creating processes and tools across these different tools can be diffic
### In some scenarios, containers do not add much
-Mainly after the rise of the Go programming language, distributing binaries has become easier.
-Distributing binaries on Windows and macOS has always been simpler than distributing binaries on Linux.
+Mainly after the rise of the Go programming language, distributing binaries has become easier. Distributing binaries on Windows and macOS has always been simpler than distributing binaries on Linux.
However, nowadays many programming languages can create binaries that can be downloaded and executed on most Linux distributions.
@@ -80,8 +68,7 @@ One of the main benefits of Docker has been ease of distribution of software, bu
### Beware container images
-Much software is distributed nowadays as container images.
-The abundance of container images means that learning how to use containers helps you run a wide variety of software distributed as a container image.
+Much software is distributed nowadays as container images. The abundance of container images means that learning how to use containers helps you run a wide variety of software distributed as a container image.
However, many container images are not of great quality, nor are adequately updated.
@@ -105,10 +92,9 @@ Finding the right combination that makes software portable can require significa
### Some container-related software has good and unique ideas
-For example, the controversial Kubernetes still provides a distributed standardized operating system that can be managed in a declarative way.
-This is a powerful concept, and still the preferred way to package software for Kubernetes depends on container images.
+For example, the controversial Kubernetes still provides a distributed standardized operating system that can be managed in a declarative way. This is a powerful concept, and still the preferred way to package software for Kubernetes depends on container images.
## What to use instead of container images
* Binaries
-* "Portable-friendly" development tools such as Go, `uv`, or Cargo.
+* "Portable-friendly" development tools such as Go, uv, or Cargo.
diff --git a/programming/crud_is_an_important_unsolved_problem.md b/blog/content/notes/tech/crud-is-an-important-unsolved-problem.gmi
index 88497f7e..c338f785 100644
--- a/programming/crud_is_an_important_unsolved_problem.md
+++ b/blog/content/notes/tech/crud-is-an-important-unsolved-problem.gmi
@@ -9,7 +9,6 @@ Although programmers have been written a huge amount of CRUD systems for decades
There are two major approaches to implementing CRUD systems:
* Traditional programming: combining a relational database and most existing programming languages enables programmers to create CRUD systems.
-
* "No code" (or "low code"): many products and services enable non-programmers to describe their data structure and the user interface requiring less technical knowledge than the traditional programming approach.
## About implementing CRUD systems with traditional programming
@@ -20,24 +19,15 @@ Most of what you need to do when using Django is to describe what you need, inst
Out of the box, Django provides:
-* List and detail views, including nested views.
- Many systems provide "flat" details view where you can edit a record, but not associated records.
- For example, they provide a detail view for customers where you can edit the customer name and other information, but any "multiple" information, such as multiple addresses or phone numbers, must be edited in a different view.
- This is frequently a huge issue, and it can require writing a significant amount of code in other systems.
- With Django, you can implement this by describing the associated data.
-
-* Multi user authentication and role-based authentication.
- With Django and without programming any code, administrators can create groups, assign users to groups, and limit the kinds of records that each group can view or edit.
-
-* Primitive change tracking.
- Out of the box, changes to records are tracked automatically and can be consulted.
+* List and detail views, including nested views. Many systems provide "flat" details view where you can edit a record, but not associated records. For example, they provide a detail view for customers where you can edit the customer name and other information, but any "multiple" information, such as multiple addresses or phone numbers, must be edited in a different view. This is frequently a huge issue, and it can require writing a significant amount of code in other systems. With Django, you can implement this by describing the associated data.
+* Multi user authentication and role-based authentication. With Django and without programming any code, administrators can create groups, assign users to groups, and limit the kinds of records that each group can view or edit.
+* Primitive change tracking. Out of the box, changes to records are tracked automatically and can be consulted.
For most CRUD implementations, alternative platforms require significantly more effort to implement those features.
Additionally, the entire stack is open source software that does not require paying licenses.
-(Surprisingly, in the past there existed even more sophisticated CRUD platforms.
- But sadly, most have disappeared.)
+(Surprisingly, in the past there existed even more sophisticated CRUD platforms. But sadly, most have disappeared.)
## About implementing CRUD systems with no code
@@ -51,8 +41,7 @@ However, because those systems focus on no code usage, frequently you hit roadbl
When you need a feature that they do not provide, it is either impossible to do it, or it requires programming in an unfriendly environment.
-Programming CRUD features can be complex.
-While traditional programming tools have evolved providing many features such as automated testing and advanced code revision control systems (rolling back bad changes and others), no code CRUD platforms do not reuse the wealth of programming tools that have been developed for traditional programming.
+Programming CRUD features can be complex. While traditional programming tools have evolved providing many features such as automated testing and advanced code revision control systems (rolling back bad changes and others), no code CRUD platforms do not reuse the wealth of programming tools that have been developed for traditional programming.
Non-developers frequently face huge challenges going beyond the basics of what the tool provides, and developers struggle and suffer by working in environments that are more limiting compared to others.
@@ -72,8 +61,7 @@ In most cases, organizations cannot justify the costs of tailoring the CRUD syst
### Improving existing traditional programming CRUD platforms
-I believe systems such as Django can still see many improvements.
-Likely, both the amount of technical knowledge to use these systems and the amount of effort to design CRUD systems can be reduced significantly.
+I believe systems such as Django can still see many improvements. Likely, both the amount of technical knowledge to use these systems and the amount of effort to design CRUD systems can be reduced significantly.
### Providing systems to transition from no code approaches to traditional programming
@@ -83,10 +71,8 @@ However, no code platforms cannot provide all features needed, and in many cases
Providing a way to migrate to a traditional programming approach would enable breaking this barrier and scaling systems more effectively.
-(Some no code platforms have APIs.
- With them, programmers can write code to extend the no code CRUD systems using traditional programming approaches.
- However, implementing functionalities through APIs has limitations and specific problems.)
+(Some no code platforms have APIs. With them, programmers can write code to extend the no code CRUD systems using traditional programming approaches. However, implementing functionalities through APIs has limitations and specific problems.)
## Further reading
-* [About Django](python/about_django.md).
+=> about-django About Django
diff --git a/blog/content/notes/tech/gadgets/pocket-computers.gmi b/blog/content/notes/tech/gadgets/pocket-computers.gmi
index a31970ef..e2cd7a2f 100644
--- a/blog/content/notes/tech/gadgets/pocket-computers.gmi
+++ b/blog/content/notes/tech/gadgets/pocket-computers.gmi
@@ -8,16 +8,8 @@ The Blackberry KeyONE I used 2017-2021 was the last device I used that had this
Since then, I believe the loss of physical keyboard phones means that some uses of the smartphone have disappeared.
-=> https://www.bringbackblackberry.com/
-
## Current devices
-=> https://www.unihertz.com/products/titan-pocket Titan Pocket (owned)
-
-* The keyboard feels worse than a Blackberry
-* The keyboard has deteriorated with age (some keys require extra pressure to activate, meaning typing is painful)
-* The keyboard software is insufficient for writing in Spanish and Catalan
-
=> https://www.clicksphone.com/communicator Clicks Communicator
=> https://www.clicksphone.com/powerkeyboard Clicks Power Keyboard
=> https://keyphone.tech Keyphone
@@ -29,6 +21,14 @@ Since then, I believe the loss of physical keyboard phones means that some uses
=> https://www.tindie.com/stores/zitaotech ZitaoTech refurbs keyboards, but they seem to be permanently out of stock.
=> https://linkapus.com/ The Q25 project is a project to put an Android phone inside a Blackberry Classic shell.
+## Recent devices
+
+=> https://www.unihertz.com/products/titan-pocket Titan Pocket (owned)
+
+* The keyboard feels worse than a Blackberry and deteriorates with age (some keys require extra pressure to activate, meaning typing is painful)
+* The keyboard software is insufficient for writing in Spanish and Catalan
+* My battery swelled and I retired it
+
## Obstacles
### Communication platforms without an open API
diff --git a/blog/content/notes/tech/git-advice.gmi b/blog/content/notes/tech/git-advice.gmi
new file mode 100644
index 00000000..8ec41105
--- /dev/null
+++ b/blog/content/notes/tech/git-advice.gmi
@@ -0,0 +1,21 @@
+# Git advice
+
+## Never use "git commit -m", use "git commit -v"
+
+Configure your system so that the EDITOR environment variable refers to your preferred editor.
+
+With "git commit -v" you can see your commit diff while writing your commit message. This helps you review that your commit is correct and write a better commit message.
+
+## Use gitignore properly
+
+=> https://git-scm.com/docs/gitignore gitignore
+
+Note that by default, Git defaults to $XDG_CONFIG_HOME/git/ignore or $HOME/.config/git/ignore.
+
+## Use the modern Git commands (or teach them)
+
+Particularly, "git checkout" has many functionalities that now can be handled by more focused commands like "git switch" and "git reset".
+
+If you have too much muscle memory and are used to them, then consider learning them only to teach other people so that they start with the safer commands.
+
+Many Git commands print suggestions that use the newer commands.
diff --git a/blog/content/notes/tech/github-annoyances.gmi b/blog/content/notes/tech/github-annoyances.gmi
new file mode 100644
index 00000000..a36214b9
--- /dev/null
+++ b/blog/content/notes/tech/github-annoyances.gmi
@@ -0,0 +1,7 @@
+# GitHub annoyances
+
+## The repository creation wizard can be confusing initially
+
+When creating a new repo, GitHub offers you to populate the repository with some files (a README, a .gitignore file, a license).
+
+In some situations, you have an existing directory in your computer with files that you want to be the initial contents of the repo. If you create a truly empty repo, then GitHub displays some instructions that can help pushing the contents of your existing directory to the new repo. If you use the GitHub features to populate the repo, then GitHub does not display these instructions and uploading your files requires more knowledge.
diff --git a/blog/content/notes/tech/misc-python-stuff.gmi b/blog/content/notes/tech/misc-python-stuff.gmi
new file mode 100644
index 00000000..f7cc33e7
--- /dev/null
+++ b/blog/content/notes/tech/misc-python-stuff.gmi
@@ -0,0 +1,62 @@
+# Misc Python stuff
+
+## Tools
+
+* Use uv for your software. It's modern and good.
+* pipx is packaged by many distros and useful for installing Python software. Provide instructions for installing your software using pipx.
+* Use pytest for testing. It's PEP-8 compliant unlike unittest in the standard library. doctest is good too.
+* Use ruff for validation.
+
+## Python versions
+
+Try to support Python versions in popular LTS distros such as RHEL and its clones, and Debian and its derivates (like Ubuntu).
+
+## Writing command line tools
+
+* Use entry points so that when using pipx or uv to install your tool, they install the tool to the user's path.
+
+### Nice Python libraries
+
+* appdirs for using the proper configuration, cache, etc. directories on multiple platforms
+* keyring for cross-platform secret storage
+* tqdm for progress bars
+
+### Writing dependencyless Python
+
+If you can write your program without using dependencies, then it automatically becomes much easier to distribute and run. This is quite possible for many tools! And helps you replace problematic shell scripts.
+
+The standard library still includes a lot of batteries:
+
+* argparse is clunkier that third-party libraries, but it works well enough. The documentation provides a pattern for subcommands under "one particularly effective way of handling sub-commands...". argcomplete is a third party library that adds tab completion for argparse programs. Review the types that argparse provides support for, such as Path, enumerated choices, etc.
+* compression allows working with many archive formats without shelling out to tar or (un)zip.
+* concurrent.futures for executing things in parallel, esp. the map function combined with a ThreadPoolExecutor (for IO-bound tasks) or ProcessPoolExecutor (to avoid the GIL in tasks that use the CPU).
+* getpass to read passwords from the terminal properly. Also to obtain the current user.
+* hashlib to avoid shelling out to commands such as sha256sum.
+* http.server is useful for simple web servers (and also as a quick web server in the command line).
+* json is about the only structured format supported in the standard library.
+* logging to print output with timestamps.
+* pathlib for any kind of path manipulation, esp. the read|write_text|bytes methods that are so convenient. shutil still contains a few functions missing from pathlib, esp. in older Python versions.
+* textwrap.dedent and str.[lr]strip for embedding multiline strings in code.
+* urllib.request is clunkier than third-party libraries, but it's usable.
+
+For very simple stuff, tkinter can implement simple graphical tools and wsgiref can implement simple web apps (that you can even deploy with CGI).
+
+### Subprocess
+
+The main problem of using Python for scripting is that the subprocess functions do not default to check=True and shell=False.
+
+Likely many of your scripts will start with a subprocess wrapper suited to your script.
+
+You can use shlex.join to print commands you execute in a copy-pastable way.
+
+## Writing scrapers
+
+=> https://playwright.dev/python/ Use Playwright
+
+* Playwright automatically sets up headless browsers.
+* Provides convenient abstractions for locating elements in a page (mostly no XPath required. It can match "intelligently" using text).
+* Has a handy UI tool that records your actions in a browser and writes equivalent *readable* Playwright code.
+
+Further reading:
+
+=> https://new.pythonforengineers.com/blog/web-automation-dont-use-selenium-use-playwright/ Web automation: don't use Selenium, use Playwright
diff --git a/blog/content/notes/tech/motivating-example-for-logical-replication-for-dynamic-ui.gmi b/blog/content/notes/tech/motivating-example-for-logical-replication-for-dynamic-ui.gmi
new file mode 100644
index 00000000..daf5329e
--- /dev/null
+++ b/blog/content/notes/tech/motivating-example-for-logical-replication-for-dynamic-ui.gmi
@@ -0,0 +1,61 @@
+# Motivating example for logical replication with dynamic UI
+
+(I'm almost sure what I write below is a horrible idea that will melt a PostgreSQL server with very few "real-time queries" at the same time. I'm very curious about how much load could PostgreSQL handle efficiently using this schema.)
+
+Suppose the following database schema (pseudo-SQL):
+
+```sql
+create table chat_messages (
+ id serial primary key,
+ posted timestamp not null,
+ channel text not null references chats(id),
+ author text not null references users(id),
+ message text not null
+);
+```
+
+Imagine you could write an UI element that subscribed to the following publication:
+
+```
+create publication foo for table chat_messages where (channel in :list_of_channels_user_is_in and posted > :some_time_ago);
+```
+
+Without writing any additional code, the UI element would get instantly notified not only of all new messages, but also of editions, deletions, or messages moved in or out of the subscribed channels. I believe you could write a real-time UI element with much shorter and safer code than any alternative I can think of that only uses OSS code. (As far as I know, [ksqlDB](https://github.com/confluentinc/ksql) does a similar thing, but has non-OSS bits and seems much harder to deploy than PostgreSQL, besides you would also need to deploy PostgreSQL.)
+
+This has some caveats:
+
+* Publications cannot do "joins", and implementing any live UI element that requires joins would be much more complex. (And I'm not sure it would still be the best way to implement things.)
+* This likely cannot be implemented efficiently without having all working set data in RAM (e.g. all the data involved in all subscriptions).
+
+My idea is writing:
+
+* A daemon that provides an API that can be used as in the following example:
+
+```
+subscription = subscribe("chat_messages", column("channel").in(list_of_channels) and column("posted").gt(some_time_ago))
+while update = subscription.next():
+ for chat_message in sorted(update.all_current_results(), key=lambda chat_message: chat_message.posted):
+ print(chat_message.current_values, chat_messages.previous_values)
+ print(update.deleted_since_last_update_results())
+```
+
+* Libraries for stacks such as "Django + HTMX", "GTK", etc. that allow to build UI elements that use the daemon API underneath, so you could write things like:
+
+```
+<ul class="channels">
+ {% foreach channel in joined_channels %}
+ <li>
+ {{ channel.name }}
+ last message: {{ for chat_message in update_all_current_results() if chat_message.current_values.channel == channel | max(lambda chat_message: chat_message.current_values.posted) }}
+ </li>
+ {% end foreach %}
+</ul>
+
+<ul class="current_chat_messages">
+ {% foreach chat_message in update.all_current_results() if chat_message.current_values.channel == current_channel %}
+ <li>{{ chat_message.current_values.author }} {{ chat_message.current_values.message }}</li>
+ {% end foreach %}
+</ul>
+```
+
+For stacks such as Django/HTMX, in simpler websites you could have websites that degrade gracefully out of the box without JS, just losing real-time updates.
diff --git a/programming/prolog_vs_sql.md b/blog/content/notes/tech/prolog-vs-sql.gmi
index beb92c01..181ae7e3 100644
--- a/programming/prolog_vs_sql.md
+++ b/blog/content/notes/tech/prolog-vs-sql.gmi
@@ -1,15 +1,12 @@
# Showing the similarities between SQL and Prolog
-SQL is a very common programming language, which sometimes is compared to the relatively more obscure Prolog language.
-Both are examples of declarative languages, where you define some facts, then you can ask questions about those facts, and the system answers the questions without you writing an explicit program.
+SQL is a very common programming language, which sometimes is compared to the relatively more obscure Prolog language. Both are examples of declarative languages, where you define some facts, then you can ask questions about those facts, and the system answers the questions without you writing an explicit program.
-However, I could not find a good example of their similarities.
-This text presents the most typical Prolog example, and translates it to SQL.
+However, I could not find a good example of their similarities. This text presents the most typical Prolog example, and translates it to SQL.
## A typical Prolog example
-`[x]` reads Prolog facts from file `x`.
-We can use the special file `user` to read facts from the REPL, ending the facts with ctrl+d:
+"[x]" reads Prolog facts from file "x". We can use the special file "user" to read facts from the REPL, ending the facts with ctrl+d:
```
@@ -23,8 +20,7 @@ $ swipl
true.
```
-You should read `father(X,Y)` as "`X` is the father of `Y`".
-So Jim is the father of Julian, and so on.
+You should read "father(X,Y)" as "X is the father of Y". So Jim is the father of Julian, and so on.
We can ask Prolog questions:
@@ -33,8 +29,7 @@ We can ask Prolog questions:
false.
```
-Is Julian the father of Jim? There is no known fact about this, so no.
-But Julian *is* the father of Joe:
+Is Julian the father of Jim? There is no known fact about this, so no. But Julian *is* the father of Joe:
```
?- father(julian, joe).
@@ -49,7 +44,7 @@ X = joe ;
X = jerome.
```
-(You press `;` to get further answers.)
+(You press ; to get further answers.)
## A simple translation to SQL
@@ -86,8 +81,7 @@ The typical example continues with some logic:
true.
```
-`X` is the grandfather of `Y` if `X` is the father of `Z`, and `Z` is the father of `Y`.
-Then you can ask questions, and Prolog knows the answers:
+X is the grandfather of Y if X is the father of Z and Z is the father of Y. Then you can ask questions, and Prolog knows the answers:
```
?- grandfather(jim, X).
diff --git a/blog/content/notes/tech/python-modules-primer.gmi b/blog/content/notes/tech/python-modules-primer.gmi
new file mode 100644
index 00000000..67d03e19
--- /dev/null
+++ b/blog/content/notes/tech/python-modules-primer.gmi
@@ -0,0 +1,229 @@
+# Python Modules Primer
+
+## Prerequisites
+
+These instructions assume a Linux environment. A macOS environment is similar, but not identical. A Windows environment is more different.
+
+## Previous knowledge
+
+### A refresher on the PATH variable
+
+If you execute the following command in your terminal:
+
+```
+$ echo hello
+```
+
+, the shell searches for the echo command in the directories listed in your PATH environment variable. You can display your PATH variable by running:
+
+```
+$ echo $PATH
+/home/user/.local/bin:/home/user/bin:/usr/share/Modules/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin
+```
+
+The contents of the PATH variable depend on your particular environment.
+
+If you run the following command:
+
+```
+$ which echo
+/usr/bin/echo
+```
+
+The which command prints where the shell locates the echo command.
+
+### A refresher on shell scripts
+
+If you create a file named foo.sh with the following contents:
+
+```
+#!/bin/sh
+
+echo hello
+```
+
+, you define a "shell script". The first line indicates that this shell script is executed by using the /bin/sh command. The rest of the file are commands to be executed by the shell command. These commands behave as if you typed them into your terminal, so if you execute this script, the command "echo hello" will be executed, printing hello.
+
+If you try to run foo.sh like you run the echo command, by typing its name, it does not work:
+
+```
+$ foo.sh
+bash: foo.sh: command not found...
+```
+
+, because the shell looks for the foo.sh in the directories listed in the PATH variable. Unless you created the foo.sh file in a directory like /usr/bin, the shell will not find the foo.sh command.
+
+A solution to this problem is to specify the path to the foo.sh file, instead of relying on the PATH variable. However, if you do this, you face a second problem.
+
+```
+$ ./foo.sh
+bash: ./foo.sh: Permission denied
+```
+
+This happens because only files with the executable permission can be executed in this way. To solve this, add the executable permission; then it works:
+
+```
+$ chmod +x foo.sh
+$ ./foo.sh
+hello
+```
+
+## The import statement in Python
+
+### Importing from the Python standard library
+
+Run the following commands by using the Python REPL:
+
+```
+$ python3
+>>> import datetime
+>>> datetime.datetime.now()
+datetime.datetime(2023, 9, 11, 21, 53, 16, 331236)
+```
+
+import works in a similar way to running a command in the shell.
+Python searches a number of directories looking for the datetime module.
+
+To see which directories are searched, run:
+
+```
+$ python3
+>>> import sys
+>>> sys.path
+['', '/usr/lib64/python39.zip', '/usr/lib64/python3.9', '/usr/lib64/python3.9/lib-dynload', '/home/alex/.local/lib/python3.9/site-packages', '/usr/lib64/python3.9/site-packages', '/usr/lib/python3.9/site-packages']
+```
+
+sys.path is a list of the directories that the import command searches.
+The contents of `sys.path` depend on your operating system and Python installation method.
+
+In my system, the /usr/lib64/python3.9 directory contains the datetime.py module.
+
+```
+$ head /usr/lib64/python3.9/datetime.py
+"""Concrete date/time and related types.
+
+See http://www.iana.org/time-zones/repository/tz-link.html for
+time zone and DST data sources.
+"""
+
+__all__ = ("date", "datetime", "time", "timedelta", "timezone", "tzinfo",
+ "MINYEAR", "MAXYEAR")
+...
+```
+
+/usr/lib64/python3.9 contains the modules in the Python standard library.
+
+### Importing your Python files
+
+If you create a file with the a.py name:
+
+```
+def f():
+ return 2
+```
+
+, and another with the b.py name:
+
+```
+import a
+
+print(a.f())
+```
+
+, then:
+
+```
+$ python b.py
+2
+```
+
+
+This works, because sys.path contains '', which means "the current directory".
+
+(sys.path is very similar to the PATH variable. However, sys.path contains the current directory by default, whereas PATH does not.)
+
+When "import a" is executed, then Python searches the directories in sys.path for an a.py file; it is found when checking the '' path. When "import datetime" is executed, Python searches in the current directory (because '' comes first in the path), doesn't find it, but then finds it in the following /usr/lib64/python3.9 directory. Python iterates over the sys.path directories, and loads the first matching file.
+
+## Installing libraries
+
+When writing Python software, sometimes it is enough with the modules included in the standard library. However, frequently you want to use other libraries. To use Python libraries, you must install them using the pip program.
+
+The pip program is not part of the python3 package in some Linux distributions, and comes from the python3-pip package.
+
+The pip program can download libraries from pypi.org, the Python package index, and install them. pip installs libraries to a "Python environment".
+
+Old versions of pip defaulted to installing libraries to the "system" Python environment. In a Linux system, the system Python environment is located in a directory such as /usr/lib64/python3.9. By default, normal Linux users cannot write to /usr, so installing a package would fail.
+
+Modern versions of pip detect that they cannot write to the "system" Python environment, and then redirect the install to the "user" Python environment. The "user" Python environment is in a directory such as ~/.local/lib/python3.9.
+
+You could use a command such as "sudo pip install" to grant pip the privileges required to write to /usr. However, this can make a Linux system unusable. Most Linux systems use software that uses the "system" Python environment. Altering the "system" Python environment can break such software. Do not run "sudo pip install" with root privileges unless you know why you need this.
+
+If you use a modern pip (or use the --user option), you can install libraries to the "user" Python environment. However, this is problematic because a Python environment can only contain a single version of a Python library. If you have two different Python programs that different versions of the same library, then these two programs cannot coexist in the "user" Python environment.
+
+In general, Python virtual environments are used to address this problem.
+
+## Creating Python virtual environments
+
+If you run:
+
+```
+$ python3 -m venv <some path>
+```
+
+This will create a directory with the path you specify, with the following contents:
+
+```
+<some path>
+├── bin
+│   ├── activate
+│   ├── pip
+│   ├── python
+├── include
+├── lib
+│   └── python3.9
+```
+
+The python and pip commands are copies of the same commands from the "system" Python environment.
+
+But these commands work differently from the "system" Python environment commands:
+
+```
+$ <some path>/bin/python
+>>> import sys
+>>> sys.path
+['', '/usr/lib64/python39.zip', '/usr/lib64/python3.9', '/usr/lib64/python3.9/lib-dynload', '<some path>/lib64/python3.9/site-packages', '<some path>/lib/python3.9/site-packages']
+```
+
+sys.path uses the lib directories in the virtual environment.
+
+When you use the `pip` program from the virtual environment, it installs the libraries to the virtual environment.
+
+You can create as many virtual environments as you need, and you can install different versions of libraries to each virtual environment.
+
+## Activating Python environments
+
+You can run the python and pip commands by specifying the full path, like we did when executing the foo.sh command earlier.
+
+By default, if you run python, the shell will invoke the python command from the "system" Python environment because it is in a directory included in the PATH variable. If you specify the full path, you override this.
+
+To save typing, the bin directory of a virtual environment contains an activate file. The activate file is a "special" shell script that must be invoked like this:
+
+```
+$ . <some path>/bin/activate
+```
+
+. is a special shell command that the activate script requires to work correctly.
+
+activate alters your path, so that the bin directory in your virtual environment comes first in your path.
+
+```
+$ echo $PATH
+/home/user/.local/bin:/home/user/bin:/usr/share/Modules/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin
+$ . <some path>/bin/activate
+(some path) $ echo $PATH
+<some path>/bin:/home/user/.local/bin:/home/user/bin:/usr/share/Modules/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin
+```
+
+, and thus if you run python, <some path>/bin/python will be executed instead of /usr/bin/python.
+
+Besides changing your prompt to indicate the virtual environment is activated, activate only alters your PATH. activate is not mandatory to use a virtual environment. For example, when running the Python command, if you specify the path of the Python executable in a virtual environment, the command will execute as if the virtual environment had been activated. Virtual environment management tools also have commands that can run commands inside a virtual environment without activating it. Activation can save time, but it is also more error-prone than more explicit means of using virtual environments.
diff --git a/programming/so_you_want_to_play_with_functional_programming.md b/blog/content/notes/tech/so-you-want-to-play-with-functional-programming.gmi
index ecf2cce3..2e8abec5 100644
--- a/programming/so_you_want_to_play_with_functional_programming.md
+++ b/blog/content/notes/tech/so-you-want-to-play-with-functional-programming.gmi
@@ -1,13 +1,10 @@
# So you want to play with functional programming
-If you are a programmer working on popular languages such as Python or Java, you are likely to have read articles about "functional programming".
-These articles can give you the idea that learning functional programming improves your skills as a programmer.
-I share this opinion.
+If you are a programmer working on popular languages such as Python or Java, you are likely to have read articles about "functional programming". These articles can give you the idea that learning functional programming improves your skills as a programmer. I share this opinion.
This article tries to help people who have read about functional programming figure out how to proceed.
-Note that this article expresses personal opinion.
-Particularly, I am not an expert in this topic:
+Note that this article expresses personal opinion. Particularly, I am not an expert in this topic:
* I have programmed some Haskell (about 50 Project Euler problems, plus experimentation on and off during the years).
* I have studies of SML and functional programming.
@@ -17,26 +14,18 @@ Particularly, I am not an expert in this topic:
Shortly after writing this, I was shown:
-https://technomancy.us/194
+=> https://technomancy.us/194 In which there is no such thing as a functional programming language
-I agree with most of that the article explains.
-I might extend this article with some similar ideas, but for the moment, I recommend reading that carefully before reading the rest of this article.
+I agree with most of that the article explains. I might extend this article with some similar ideas, but for the moment, I recommend reading that carefully before reading the rest of this article.
## The basics of functional programming
-[The Wikipedia article on functional programming](https://en.wikipedia.org/wiki/Functional_programming) is a great place to get started.
+=> https://en.wikipedia.org/wiki/Functional_programming The Wikipedia article on functional programming is a great place to get started.
-The article describes a few concepts related to functional programming.
-I consider the following two the pillars of functional programming:
+The article describes a few concepts related to functional programming. I consider the following two the pillars of functional programming:
-* First-class and higher order functions.
- In languages with first-class functions, functions are values that you can use like other types such as integers.
- Higher-order functions are functions that take functions as arguments or return functions.
-
-* Pure functions.
- Pure functions always return the same value for a given set of arguments.
- Pure functions also have no side effects; they do not modify anything in the system they run.
- For example, a function that creates a file is not pure.
+* First-class and higher order functions. In languages with first-class functions, functions are values that you can use like other types such as integers. Higher-order functions are functions that take functions as arguments or return functions.
+* Pure functions. Pure functions always return the same value for a given set of arguments. Pure functions also have no side effects; they do not modify anything in the system they run. For example, a function that creates a file is not pure.
These concepts can be applied in most popular programming languages.
@@ -51,8 +40,7 @@ def twice(f):
f()
```
-`twice` is a higher order function because it takes a function as an argument.
-Functions are first-class functions because you can use `hello` as a value:
+twice is a higher order function because it takes a function as an argument. Functions are first-class functions because you can use hello as a value:
```
>>> twice(hello)
@@ -62,36 +50,24 @@ hello
Similarly, you can write pure functions in almost any language.
-When you have first-class functions, you can define some higher-order functions that generalize some common code.
-Three very common higher-order functions are:
+When you have first-class functions, you can define some higher-order functions that generalize some common code. Three very common higher-order functions are:
-* Filter.
- Filter applies a function to each element of a list, and returns a list composed of the elements for which the function returned true.
-* Map.
- Map applies a function to each element of a list, and returns a list of the result of the application of the function to each element.
-* Fold.
- A fold starts from an initial value, then calls a function with the initial value and the first element of the list.
- Then it calls the function with the result of the previous call, and the next element of the list.
- This continues until the list end, returning the last result of the function.
+* Filter applies a function to each element of a list, and returns a list composed of the elements for which the function returned true.
+* Map applies a function to each element of a list, and returns a list of the result of the application of the function to each element.
+* Fold. A fold starts from an initial value, then calls a function with the initial value and the first element of the list. Then it calls the function with the result of the previous call, and the next element of the list. This continues until the list end, returning the last result of the function.
(For example, folding with the sum operator and an initial value of 0, sums the elements of a list.)
-Note that you can implement many list manipulations by composing filters, maps, and folds with different functions.
-(And by adding more higher-order functions, you can implement more list manipulations.)
+Note that you can implement many list manipulations by composing filters, maps, and folds with different functions. (And by adding more higher-order functions, you can implement more list manipulations.)
Also, you can manipulate other data structures with equivalent or other higher-order functions.
Implementing code using higher-order functions and pure functions already has some interesting benefits.
-* Impure functions frequently require more mental overhead to understand, because you need to understand state.
- With pure functions, you do not have to think about state.
-
-* To understand a program written as a composition of functions, you can start by understanding individual functions and then understand how they fit together.
- The same program written as a sequence of statements is often more difficult to understand.
- (However, sometimes the opposite effect occurs.)
+* Impure functions frequently require more mental overhead to understand, because you need to understand state. With pure functions, you do not have to think about state.
+* To understand a program written as a composition of functions, you can start by understanding individual functions and then understand how they fit together. The same program written as a sequence of statements is often more difficult to understand. (However, sometimes the opposite effect occurs.)
-You can use these concepts in most popular programming languages.
-(Most popular languages also provide higher-order functions such as filters, maps, and folds.)
+You can use these concepts in most popular programming languages. (Most popular languages also provide higher-order functions such as filters, maps, and folds.)
So you can get started with functional programming by using the programming languages you already know:
@@ -107,8 +83,7 @@ Writing code using these concepts often leads to:
* Writing cumbersome code if the programming language you use lacks certain features.
* Unlocking additional functional programming techniques.
-Therefore, many programming languages provide features that make functional programming more straightforward, or features enabled by functional programming.
-Languages providing features related to functional programming are commonly named "functional programming languages".
+Therefore, many programming languages provide features that make functional programming more straightforward, or features enabled by functional programming. Languages providing features related to functional programming are commonly named "functional programming languages".
Although you can use functional programming with non-functional programming languages, this can often lead to:
@@ -117,23 +92,18 @@ Although you can use functional programming with non-functional programming lang
### The need for powerful type systems and type inference
-Higher-order functions often have complex type requirements.
-For example, to filter a list of a given type, you must pass a function that takes a single argument of that type and returns a boolean.
-If the arguments do not have the correct types, then the code does not work correctly.
+Higher-order functions often have complex type requirements. For example, to filter a list of a given type, you must pass a function that takes a single argument of that type and returns a boolean. If the arguments do not have the correct types, then the code does not work correctly.
-In languages with dynamic types, the program fails at runtime.
-In languages with static types, you frequently must specify the types, and higher-order functions often require complex types involving different function types.
+In languages with dynamic types, the program fails at runtime. In languages with static types, you frequently must specify the types, and higher-order functions often require complex types involving different function types.
Functional programming languages frequently:
* Have static types, to prevent frequent runtime failures.
-* Automatically infer types instead of requiring programmers to declare them.
- (However, automatic type inference can cause issues in some scenarios, so frequently programming languages allow writing explicit types, or even require explicit types in some cases.)
+* Automatically infer types instead of requiring programmers to declare them. (However, automatic type inference can cause issues in some scenarios, so frequently programming languages allow writing explicit types, or even require explicit types in some cases.)
Because functional programs often use more complex types, functional programming languages often have more powerful type systems than non-functional programming languages.
-Derived from those properties, functional programming languages result in the "if it compiles, it works *correctly*" phenomenon.
-This phenomenon helps avoid incorrect programs.
+Derived from those properties, functional programming languages result in the "if it compiles, it works *correctly*" phenomenon. This phenomenon helps avoid incorrect programs.
## Functional programming languages
@@ -141,34 +111,26 @@ This phenomenon helps avoid incorrect programs.
Functional programming practitioners often recommend Haskell as a functional programming language.
-[According to the Wikipedia](https://en.wikipedia.org/wiki/Haskell), "Haskell is a general-purpose, statically-typed, purely functional programming language with type inference and lazy evaluation".
-Also, Haskell was designed by a committee whose purpose was "to consolidate existing functional languages into a common one to serve as a basis for future research in functional-language design".
+According to the Wikipedia, "Haskell is a general-purpose, statically-typed, purely functional programming language with type inference and lazy evaluation". Also, Haskell was designed by a committee whose purpose was "to consolidate existing functional languages into a common one to serve as a basis for future research in functional-language design".
-* Haskell is perhaps the language with more built-in functional programming features.
-As mentioned, Haskell is used for research about functional programming, therefore many new concepts appear in Haskell first.
+* Haskell is perhaps the language with more built-in functional programming features. As mentioned, Haskell is used for research about functional programming, therefore many new concepts appear in Haskell first.
* Haskell is also very strict about functional programming, so Haskell drives programmers more strongly towards avoiding non-functional programming.
* Haskell syntax is designed so Haskell programs can be extremely terse and contain almost no extraneous syntax.
-* Haskell is a very popular language, with a very large ecosystem.
- You can take advantage of many existing libraries and tools for developing real-world programs faster.
+* Haskell is a very popular language, with a very large ecosystem. You can take advantage of many existing libraries and tools for developing real-world programs faster.
However, Haskell's benefits frequently also are negative for learning.
-* Haskell uses "lazy" evaluation, where most programming languages use "eager" evaluation.
- Haskell does not evaluate expressions until needed (and might not evaluate some expressions).
- Lazy evaluation can lead to efficiency.
- However, lazy evaluation can cause unexpected performance problems.
- [Foldr Foldl Foldl'](https://wiki.haskell.org/Foldr_Foldl_Foldl%27) explains how choosing incorrectly among different implementations of fold can lead to impactful performance problems.
- When writing Haskell code for learning, you can likely stumble into issues not present in languages that use eager evaluation.
+* Haskell uses "lazy" evaluation, where most programming languages use "eager" evaluation. Haskell does not evaluate expressions until needed (and might not evaluate some expressions). Lazy evaluation can lead to efficiency and clearer programs. However, lazy evaluation can cause unexpected performance problems.
+
+=> https://wiki.haskell.org/Foldr_Foldl_Foldl%27 "Foldr Foldl Foldl'" explains how choosing incorrectly among different implementations of fold can lead to impactful performance problems.
+
+When writing Haskell code for learning, you can likely stumble into issues not present in languages that use eager evaluation.
-* Haskell is very strict about purity.
- To implement programs that have side effects, such as accessing files, you must use specific language features.
- Many articles try to explain those features, because many people have trouble understanding them.
+* Haskell is very strict about purity. To implement programs that have side effects, such as accessing files, you must use specific language features. Many articles try to explain those features, because many people have trouble understanding them.
-* Many libraries and tools in the ecosystem take advantage of powerful features enabled by Haskell.
- However, this might cause that using these libraries and tools require the understanding of the features they are based upon.
+* Many libraries and tools in the ecosystem take advantage of powerful features enabled by Haskell. However, this might cause that using these libraries and tools require the understanding of the features they are based upon.
-Also, Haskell syntax is very terse, which leads to Haskell compilers not providing clear error messages.
-For example:
+Also, Haskell syntax is very terse, which leads to Haskell compilers not providing clear error messages. For example:
```
$ ghci
@@ -186,38 +148,29 @@ $ ghci
In complex programs, programmers new to Haskell might have trouble identifying that a function has been called with an extra argument from that error message.
-Personally, Haskell is my favorite functional programming language.
-However, I learned Haskell after learning (with teachers and support from others) other functional programming languages.
-I think that Haskell is ideal to learn the most powerful concepts in functional programming, but it is not as ideal as a first functional programming language.
+Personally, Haskell is my favorite functional programming language. However, I learned Haskell after learning (with teachers and support from others) other functional programming languages. I think that Haskell is ideal to learn the most powerful concepts in functional programming, but it is not as ideal as a first functional programming language.
(Note that these recommendations come from someone who only has implemented about 50 Project Euler problems in Haskell, and has experimented on and off with the language, but not been paid for it.)
### Lisp
-Many programmers like Lisp and languages in the Lisp family, such as Scheme or Clojure.
-Lisp programmers often recommend Lisp to learn functional programming.
+Many programmers like Lisp and languages in the Lisp family, such as Scheme or Clojure. Lisp programmers often recommend Lisp to learn functional programming.
-Lisp is a very minimalistic, yet infinitely flexible language.
-Lisp is extensible, so you can add most programming language features to Lisp, including functional programming features.
+Lisp is a very minimalistic, yet infinitely flexible language. Lisp is extensible, so you can add most programming language features to Lisp, including functional programming features.
Therefore, you can do functional programming in Lisp, and also benefit from all other Lisp features.
However, languages in the Lisp family tend to not have static typing and associated features, thus do not frequently exhibit the "if it compiles, it works *correctly*" phenomenon.
-Lisp has one of the simplest syntaxes of any programming language.
-The simple syntax of Lisp is directly tied to its power.
-Many favor the Lisp syntax and argue that the syntax makes Lisp better for learning programming.
-Personally, I find the Lisp syntax hard to read and write, and likely an additional difficulty on top of learning functional programming.
+Lisp has one of the simplest syntaxes of any programming language. The simple syntax of Lisp is directly tied to its power. Many favor the Lisp syntax and argue that the syntax makes Lisp better for learning programming. Personally, I find the Lisp syntax hard to read and write, and likely an additional difficulty on top of learning functional programming.
-I recommend learning Lisp because it is a unique programming language that can teach you many programming language concepts that are not present in many other languages.
-However, I do not recommend Lisp for learning functional programming (unless you already know Lisp).
+I recommend learning Lisp because it is a unique programming language that can teach you many programming language concepts that are not present in many other languages. However, I do not recommend Lisp for learning functional programming (unless you already know Lisp).
(Note that these recommendations come from someone who has some formal training on Lisp but only uses Lisp infrequently [as a recent Emacs user].)
### The ML family of programming languages
-ML is a language that appeared in 1973.
-Since then, three dialects have become the most popular implementations of ML:
+ML is a language that appeared in 1973. Since then, three dialects have become the most popular implementations of ML:
* OCaml
* Standard ML
@@ -243,7 +196,6 @@ Error: This function has type int -> int -> int
It is applied to too many arguments; maybe you forgot a `;'.
```
-In my opinion, OCaml and F# are better languages for the initial learning of functional programming than Haskell.
-After learning an ML, you are likely more prepared to learn Haskell and more sophisticated functional programming.
+In my opinion, OCaml and F# are better languages for the initial learning of functional programming than Haskell. After learning an ML, you are likely more prepared to learn Haskell and more sophisticated functional programming.
(Note that those recommendations come from someone who only has experimented with OCaml and F#, and learned SML formally.)
diff --git a/blog/content/notes/tech/take-the-less-traveled-road.gmi b/blog/content/notes/tech/take-the-less-traveled-road.gmi
new file mode 100644
index 00000000..e9107470
--- /dev/null
+++ b/blog/content/notes/tech/take-the-less-traveled-road.gmi
@@ -0,0 +1,29 @@
+# Take the less traveled road
+
+> Two roads diverged in a wood, and I—
+
+> I took the one less traveled by,
+
+> And that has made all the difference.
+
+=> https://www.gutenberg.org/cache/epub/59824/pg59824-images.html#THE_ROAD_NOT_TAKEN Robert Frost, The Road Not Taken
+
+=> https://xkcd.com/743/ Infrastructures, by Randall Munroe
+
+The prisoner's dilemma describes situations where people can choose to act in their own interest or collaborate with others. I believe the prisoner's dilemma is present everywhere in our daily life and can explain many behaviors and situations.
+
+One such scenario is when we choose services to use on the Internet.
+
+Frequently, we choose services that are easy, popular, and even have no cost.
+
+In the past, even non-technical people got shared hosting and ran WordPress to blog, with nearly total control about their communication. Nowadays, mostly everyone uses something like Twitter and cedes control to a company.
+
+Using Twitter is easier than self-hosting WordPress. Twitter is so popular, that your message will likely reach more users on Twitter than on an independent blog. And you can use Twitter for free, whereas shared hosting costs you money and time.
+
+Twitter became dominant and nowadays, many are frustrated by how Twitter has changed.
+
+Dominance implies lack of competition, which is nearly always bad for the consumer.
+
+Which means choosing which services to use frequently is a difficult choice.
+
+We all have limited time and energy, but I propose that whenever you can, you should take the less traveled road. The easy alternative likely benefits you in the short term, but can easily contribute to a monoculture that will damage everyone in the end, including you.
diff --git a/programming/the_tragedy_of_the_geeks.md b/blog/content/notes/tech/the-tragedy-of-the-geeks.gmi
index c1193d5f..e5ce1b10 100644
--- a/programming/the_tragedy_of_the_geeks.md
+++ b/blog/content/notes/tech/the-tragedy-of-the-geeks.gmi
@@ -1,12 +1,10 @@
# The tragedy of the geeks
-Since the first computer entered our home, I was hooked.
-This happened more than four decades ago, and continuously tinkering with computers has given me a well-paid and comfortable job.
+Since the first computer entered our home, I was hooked. This happened more than four decades ago, and continuously tinkering with computers has given me a well-paid and comfortable job.
However, getting such jobs seems linked to spending a significant amount of your personal time practicing your skills.
-Many people seek careers related to computing because jobs have attractive conditions.
-However, they might later regret the time and energies spent trying to get into the field when they learn that getting a good job requires unexpected effort.
+Many people seek careers related to computing because jobs have attractive conditions. However, they might later regret the time and energies spent trying to get into the field when they learn that getting a good job requires unexpected effort.
This document tries to explain to people who want to work with computers this phenomenon, to help them make a better decision.
@@ -18,18 +16,15 @@ Working with computers is the only career I can think of where all of the follow
* There is a reputation of abundant well-paid job offers with good conditions.
* Working on personal projects sounds fun.
-This means that many of us end up spending a significant amount of time working on personal projects.
-This time investment increases our skills and the things we know.
+This means that many of us end up spending a significant amount of time working on personal projects. This time investment increases our skills and the things we know.
## Hiring
Hiring is one of the most highly debated topics in this industry.
-Many people believe that many candidates cannot do the job.
-There are many stories about new hires who cannot write simple programs.
+Many people believe that many candidates cannot do the job. There are many stories about new hires who cannot write simple programs.
-Whether this is common or not is not as important as whether people making decisions believe there are large differences between candidates.
-When people who hire think that their hiring decision is going to have a large effect on them, then they want to make sure that they pick the right person.
+Whether this is common or not is not as important as whether people making decisions believe there are large differences between candidates. When people who hire think that their hiring decision is going to have a large effect on them, then they want to make sure that they pick the right person.
My perception is that most of the organizations that offer good job conditions (and many who do not) try to be very selective in hiring people.
@@ -37,8 +32,7 @@ My perception is that most of the organizations that offer good job conditions (
When you are hiring people, candidates who have spent significant time on personal projects tend to stand out over candidates who have not.
-This improved perception during the hiring process does not necessarily relate to improved performance on the job.
-However, I believe that people who tinker on their spare time tend to land better jobs.
+This improved perception during the hiring process does not necessarily relate to improved performance on the job. However, I believe that people who tinker on their spare time tend to land better jobs.
## Handing out advice
@@ -46,20 +40,17 @@ Because there are good jobs working with computers, many people think about maki
There are many curriculums and formal education programs, from shorter (typically one year) to longer (four or five years).
-Some of them provide advice to land a good job, and students who follow programs who do not, tend to ask for advice.
-In any case, one of the most frequent pieces of advice on the topic, is tinkering on your own time.
+Some of them provide advice to land a good job, and students who follow programs who do not, tend to ask for advice. In any case, one of the most frequent pieces of advice on the topic, is tinkering on your own time.
I believe this is actually good advice, as in that it's more likely to be an efficient way to increase your prospects.
-However, remember that hiring is roughly a competitive process.
-An organization evaluates a group of candidates, and tries to pick the best one.
+However, remember that hiring is roughly a competitive process. An organization evaluates a group of candidates, and tries to pick the best one.
So to stand out, if more candidates tinker (because this is effective advice), the more you need to tinker to stand out.
I cannot estimate how much you need to tinker on your own time to land a good job, but my guess is that it is more than what someone wanting to get into the field expects.
-As long as this dynamic continues, the tinkering required to land a good job will increase.
-Only reduced competition can reduce the tinkering required, and reduced competition can happen by few factors, such as increased demand for workers, or a reduction in job seekers.
+As long as this dynamic continues, the tinkering required to land a good job will increase. Only reduced competition can reduce the tinkering required, and reduced competition can happen by few factors, such as increased demand for workers, or a reduction in job seekers.
## Breaking the cycle
@@ -67,9 +58,8 @@ I cannot think of much that we can individually do to break the cycle.
Maybe if people coming into the field are aware of this phenomenon, they will be able to make a better decision about what to do.
-If a sufficient amount of people decide that the time investment is not worthwhile, then perhaps the competition will decrease.
-And if people are well informed and decide to move forward, at least they will be less likely to become frustrated or regret their decision.
+If a sufficient amount of people decide that the time investment is not worthwhile, then perhaps the competition will decrease. And if people are well informed and decide to move forward, at least they will be less likely to become frustrated or regret their decision.
## Further reading
-* [A paean to programming](https://bertrandmeyer.com/2025/04/23/a-paean-to-programming/), by [Bertrand Meyer](https://en.wikipedia.org/wiki/Bertrand_Meyer)
+=> https://bertrandmeyer.com/2025/04/23/a-paean-to-programming/ A paean to programming, by Bertrand Meyer
diff --git a/blog/post-receive b/blog/post-receive
index 0721ce3b..f69abaf6 100755
--- a/blog/post-receive
+++ b/blog/post-receive
@@ -27,7 +27,9 @@ with tempfile.TemporaryDirectory() as tempdir:
tempdir = pathlib.Path(tempdir)
repo = tempdir / "repo"
subprocess.run(["git", "worktree", "add", repo, pushed_commit], check=True)
- blog = repo / "blog"
- os.chdir(blog)
- os.environ["PATH"] = "/home/alex/.local/bin:" + os.environ["PATH"]
- subprocess.run(["./build.sh", dest], check=True)
+ try:
+ blog = repo / "blog"
+ os.environ["PATH"] = "/home/alex/.local/bin:" + os.environ["PATH"]
+ subprocess.run(["./build.sh", dest], check=True, cwd=blog)
+ finally:
+ subprocess.run(["git", "worktree", "remove", repo], check=True)
diff --git a/emacs/emacs.el b/emacs/emacs.el
index 50639786..807d1ed2 100644
--- a/emacs/emacs.el
+++ b/emacs/emacs.el
@@ -67,9 +67,6 @@
:config
(global-kkp-mode +1))
-;; Helps eglot locate remote language servers, such as ones in ~/.local/bin
-(add-to-list 'tramp-remote-path 'tramp-own-remote-path)
-
;; Install xclip so cutting/copying in Emacs on a terminal affects the graphical clipboard
(use-package xclip
:ensure t
diff --git a/misc/comprar-bajo-en-sodio.org b/misc/comprar-bajo-en-sodio.org
deleted file mode 100644
index 9aab7273..00000000
--- a/misc/comprar-bajo-en-sodio.org
+++ /dev/null
@@ -1,178 +0,0 @@
-* Comprar bajo en sodio
-
-Es complicado comer una dieta baja en sal.
-Este documento contiene productos que:
-
-- Podríamos pensar que tienen más cantidad de sal, pero en realidad no tienen tanta.
- (Intento comer cosas con menos de 1g sal/100g, preferiblemente menos de 0,5g.)
-- Cosas que pese a no tener sal, son más sabrosas de lo que uno esperaría.
-
-DISCLAIMER:
-No soy médico ni nutricionista.
-Contrastad todo lo que recomiendo aquí, que no son más que consejos y sugerencias sobre mis experiencias, sin ningún rigor médico.
-El propósito de esta página es compartir ideas, no dar consejo médico/nutricional.
-
-ATENCIÓN:
-Listo específicamente el producto preciso y la cantidad de sal aplica solamente a ese producto.
-Dentro de productos simiares, el contenido en sal puede variar muchísimo.
-Prestad siempre atención al comprar para revisar la cantidad de sal, que puede no coincidir con la descrita aquí.
-La sal no es la única fuente de sodio, revisad si lo que coméis lleva otras sustancias con sodio como el glutamato monosódico (o MSG).
-
-** Recursos interesantes
-
-- [[https://world.openfoodfacts.org][Open Food Facts]] tiene datos nutricionales sobre muchos productos.
- Sorprendentemente, he encontrado muchos productos buscando por código de barras.
-
-** Productos
-
-*** Quesos
-
-- Alcampo - [[https://www.compraonline.alcampo.es/products/auchan-queso-arz%C3%BAa-ulloa-d-o-p-500-g-producto-alcampo/91158][AUCHAN Queso Arzúa Ulloa D.O.P. 500 g.]] - Sal 0,39/100g
-
- Este queso es el motivo principal para crear este documento.
- Este tipo de quesos parecen en general bajos en sal, y el de Alcampo tiene especialmente poca.
- Sin embargo, me gusta mucho (y me hace dudar que la información nutricional sea correcta).
-
- Los quesos tipo tetilla también tienen poca sal, pero más que este.
-
-- Burratas - Sal; Mercadona 0,61/100g
-
- Las burratas también tienen poca sal, y pueden ayudar a dar algo de vida a algún plato.
-
-- Mascarpone - Sal; Mercadona 0,11/100g
-
- El mascarpone puede servir para hacer más cremoso algún plato, sin añadir demasiada sal.
-
-- Mercadona - Queso untar light de vaca Hacendado - Sal 0,75/100g
-
- El queso de untar light de Mercadona tiene bastante más sal que otros quesos apuntados aquí, pero menos que la mayoría de quesos de untar.
- Puede ser un capricho ocasional.
-
-*** Pan
-
-- Mercadona - Piquitos bajos en sal Hacendado - Sal 0,22/100g
-
- Para su bajo contenido en sal, los picos bajos en sal de Mercadona son muy sabrosos y pueden servir para picar.
-
-- Alcampo - Pan Milagros de molde integral sin corteza - Sal 0,76/100g
-
- En general el pan de molde suele llevar bastante sal, aunque hay excepciones.
-
-- Auchan - Tortillas de trigo integrales - Sal 0,88/100g
-
- Las tortillas de trigo suelen llevar más sal, no he encontrado con menos que estas.
-
-*** Conservas
-
-- Alcampo - [[https://www.compraonline.alcampo.es/products/producto-alcampo-h%C3%ADgado-de-bacalao-ahumado-115-g/649510][Hígado de bacalao ahumado 115 g.]] - Sal 0,54/100g
-
- El hígado de bacalao es graso, pero sabroso pese a no tener mucha sal.
- Puede servir para aderezar un plato.
-
-- Atún en aceite bajo en sal - Sal; Calvo 0,16/100g, Mercadona 0,3/100g
-
- Aunque abuso un poco del atún bajo en sal, sirve para dar un poco de vida a muchos platos.
- Ojo, que la cantidad de sal puede variar mucho.
-
-- Lomos de bonito en aceite - Sal; Miau 0,59/100g
-
- Los lomos de bonito en aceite, generalmente en tarros de cristal, llevan más sal que el atún en aceite bajo en sal, pero algunos no llevan mucho más y me parecen más sabrosos.
- Ojo, que la cantidad de sal puede variar mucho (por ejemplo el de Mercadona se va a 0,84/100g).
-
-*** Platos preparados
-
-- Litoral Fabada asturiana -30% sal y grasa - Sal 0,6/100g
-- Tortilla de patata sin cebolla Auchan - Sal 0,91/100g
-- Carrilera en salsa Carretilla - Sal 0.68/100g
-- Pizza cuatro quesos Auchan - Sal 0.9/100g
-
-Aunque los platos preparados suelen tener bastante sal, a veces alguno lleva menos de lo que parece.
-
-*** Pasta
-
-- Pasta rellena - Sal; Giovanni Rana ravioli costillas a la barbacoa 0,6g/100g
-
- Muchas pastas rellenas tienen bastante sal, pero al haber bastante variedad, a veces nos encontramos sorpresas.
- No es ideal comer pasta rellena sólo hervida, pero de esta manera podemos reducir nuestro consumo de sal.
-
-*** Cereales
-
-- Weetabix Crispy Minis Choco - Sal 0,16/100g
-
- De los mejores cereales con nutri-score A, y con menos sal que otros (e.g. los Choco Krispies de Kellogg's tienen 0,67g/100g de sal).
- (Nota: recientemente pasaron a tener nutri-score B, aunque la información nutricional no cambió.)
- Si queremos desayunar cereales, estos nos permiten ahorrar un poco de sodio.
-
-*** Patatas
-
-- Patatas microondas - Sal; Mercadona 0,015/100g
-
- Las patatas de microondas se preparan rápido y combinadas con otros productos, pueden arreglar una comida rápida.
- En general, suelen llevar muy poca sal y están buenas sin añadir más, aunque es recomendable aderezarlas con otro producto sin sal.
-
-- Patatas prefritas congeladas - Sal; Mercadona Patatas prefritas Waffle fries Lambweston ultracongeladas 0,5/100g
-
- Muchas patatas prefritas congeladas no llevan mucha sal y se pueden comer perfectamente solas.
-
-- Patatas fritas sin sal - Sal; Mercadona 0,011/100g
-
- Las patatas fritas sin sal están más buenas de lo que a priori cabría esperar, y pueden ser un aperitivo más que adecuado.
-
-- Patatas Fritas en Aceite de Oliva "Arte Fritas" - Sal 0.5/100g
-
- Y no son frecuentes, pero algunas patatas fritas llevan menos sal que la mayoría.
-
-- Patatas sabor picante Alcampo - Sal 0,8/100g "bajo en sodio"
-
- Estas patatas sabor picante tipo "Pringles" dicen llevar sal baja en sodio.
-
-- Preparados de patata - Sal; Mercadona patatas bravas con allioli y salsa picante 0,37/100g, Mercadona patatas con allioli 0,6/100g
-
- Aunque llevan más sal que otros productos de esta categoría, siguen teniendo no mucha sal y pueden ser otro aperitivo.
-
-*** Arroces
-
-- Arroz cocido basmati Sabroz Brillante - Sal 0,35/100g
-
- Los vasitos de arroz pueden variar bastante en sal, pero los Sabroz no llevan muchísima.
- Se pueden combinar con otros productos para arreglar una comida rápida.
-
-*** Salsas
-
-- Mayonesa Calvé sabor casero - Sal 0,73g/100g
-
- La mayoría de mayonesas tienen bastante sal, pero hay alguna excepción.
-
-- Patak's Tikka Masala - Sal 0,72g/100g
-
- Una salsa india con una cantidad de sal moderada que puede aderezar arroz o carne.
-
-- Alioli Prima - Sal 0,72g/100g
-
- El alioli Prima no es particularmente bueno, pero no he encontrado otro allioli que no tenga mucha más sal.
-
-- Nacho Cheese Doritos - Sal 0,9g/100g
-- Salsa guacamole Old El Paso - Sal 0,8g/100g
-
-*** Especias
-
-- Curry
-
- Aunque en general no he encontrado especias que compensen comer sin sal, el curry es de lo que se acerca más.
-
-*** Snacks
-
-- Tortitas - Sal; Ecocesta 0,5/100g
-
- En general, las tortitas (de arroz, maíz, etc.) *no* son bajas en sal.
- Sin embargo, se pueden encontrar algunas con menos sal.
- En el Alcampo que uso, hay una sección con tortitas que todas tienen bastante sal, pero en la sección de alimentos dietéticos tienen las tortitas Ecocesta con menos sal.
-
-- Tortolines chifles con sal - Sal 0,5/100g
-
- Estos chips de plátano tienen algo menos de sal que la mayoría de snacks salados que encontramos en el supermercado.
-
-- Snacks y puffs Hero Eco - Sal 0-0,13/100g
-
- Hero tiene una línea de snacks para niños a partir de 8-10 meses.
- Son bastante reducidos en sal, así que son un poco sosos, pero tienen sabor y son crujientes.
diff --git a/misc/setup.md b/misc/setup.md
deleted file mode 100644
index 6f66a3d5..00000000
--- a/misc/setup.md
+++ /dev/null
@@ -1,62 +0,0 @@
-# Setup
-
-See [personal infrastructure](../personal_infra) for things I run on servers.
-
-## Workstation hardware
-
-I have two Windows machines; a pre-built gaming tower for gaming (connected to a TV) and a small Celeron PC in another location so that I always have some Windows PC physically available.
-
-Besides my employer's workstations, I do most of my computing from two desktop computers in each location.
-
-I have an old ThinkPad for tests, and a slow ChromeBook tablet with detachable keyboard.
-
-## Software
-
-I tend to use LTS Linux distributions.
-Currently I use Debian Stable, for some time I used CentOS.
-
-I use GNOME with [PaperWM](https://github.com/paperwm/PaperWM).
-
-I use Firefox with LanguageTool, Bitwarden and NoScript.
-
-* GNU Screen
-* [ubpkg](https://github.com/alexpdp7/ubpkg/) to install software
-* [Emacs](../emacs)
-* Nextcloud (through rclone/WebDAV in some systems)
-* VLC
-* Steam
-* uv for Python development
-* [`fx`](https://fx.wtf/) to explore JSON
-* Visidata for working with tables
-* [Mutagen](https://mutagen.io/) for SSH sync
-* mbsync + [my own terminal email client](https://github.com/alexpdp7/epistle) for mail backup and some reading
-* [ressh](https://github.com/alexpdp7/ressh/) to retry SSH connections
-
-## Services
-
-* Miniflux (RSS) (self-hosted)
-* Incarnator (ActivityPub/Fediverse/Mastodon), with Enafore for desktop, Toot for terminal, and Tusky for Android (self-hosted)
-* Telegram, WhatsApp, Discord, Signal, Slack, Google Messages, but also [modern IRC](../workstation/modern_irc.md)
-* TVMaze
-* Google (GMail, Calendar, Contacts)
-* YouTube Music
-* Transmission/Edonkey (P2P)
-
-## Phone
-
-I use a Pixel 7A and a Titan Pocket.
-
-- Firefox
-- F-Droid
-- Bitwarden
-- Orgzly (for Emacs org mode, synchronized via Nextcloud)
-- Bangle.js GadgetBridge (for my Bangle.js 2)
-- Termux (for occasional SSH use)
-- VLC
-- lichess
-- OpenFoodFacts
-- AnyConnect (VPN)
-- Lagrange (Gemini)
-- aNag
-- O'Reilly
-- PressReader
diff --git a/misc/take-the-less-traveled-road.md b/misc/take-the-less-traveled-road.md
deleted file mode 100644
index be5fb72b..00000000
--- a/misc/take-the-less-traveled-road.md
+++ /dev/null
@@ -1,32 +0,0 @@
-# Take the less traveled road
-
-> Two roads diverged in a wood, and I—\
-> I took the one less traveled by,\
-> And that has made all the difference.
-
-(Robert Frost, [The Road Not Taken](https://www.gutenberg.org/cache/epub/59824/pg59824-images.html#THE_ROAD_NOT_TAKEN))
-
-[Infrastructures](https://xkcd.com/743/), by Randall Munroe
-
-The prisoner's dilemma describes situations where people can choose to act in their own interest or collaborate with others.
-I believe the prisoner's dilemma is present everywhere in our daily life and can explain many behaviors and situations.
-
-One such scenario is when we choose services to use on the Internet.
-
-Frequently, we choose services that are easy, popular, and even have no cost.
-
-In the past, even non-technical people got shared hosting and ran WordPress to blog, with nearly total control about their communication.
-Nowadays, mostly everyone uses something like Twitter and cedes control to a company.
-
-Using Twitter is easier than self-hosting WordPress.
-Twitter is so popular, that your message will likely reach more users on Twitter than on an independent blog.
-And you can use Twitter for free, whereas shared hosting costs you money and time.
-
-Twitter became dominant and nowadays, many are frustrated by how Twitter has changed.
-
-Dominance implies lack of competition, which is nearly always bad for the consumer.
-
-Which means choosing which services to use frequently is a difficult choice.
-
-We all have limited time and energy, but I propose that whenever you can, you should take the less traveled road.
-The easy alternative likely benefits you in the short term, but can easily contribute to a monoculture that will damage everyone in the end, including you.
diff --git a/personal_infra/playbooks/roles/coming_soon_rss/tasks/main.yaml b/personal_infra/playbooks/roles/coming_soon_rss/tasks/main.yaml
deleted file mode 100644
index b83749de..00000000
--- a/personal_infra/playbooks/roles/coming_soon_rss/tasks/main.yaml
+++ /dev/null
@@ -1,11 +0,0 @@
----
-- uri:
- url: https://raw.githubusercontent.com/alexpdp7/coming-soon-rss/6708758549e1eaffd77a9ef556deeb4a283f5557/k8s.yaml
- return_content: true
- register: manifest
- delegate_to: 127.0.0.1
-- k8s:
- context: "{{ context }}"
- state: present
- definition: "{{ manifest.content | replace('coming-soon-rss.example.com', host) }}"
- delegate_to: 127.0.0.1
diff --git a/personal_infra/playbooks/site.yaml b/personal_infra/playbooks/site.yaml
index 2714bc77..73969cdf 100644
--- a/personal_infra/playbooks/site.yaml
+++ b/personal_infra/playbooks/site.yaml
@@ -72,15 +72,3 @@
- role: takahe
vars:
context: "admin@{{ talos_host.talos_cluster }}"
-
-- name: deploy coming-soon-rss
- hosts: k8s-prod.h1.int.pdp7.net
- tags:
- - k8s
- - coming-soon-rss
- gather_facts: false
- roles:
- - role: coming_soon_rss
- vars:
- context: "admin@{{ talos_host.talos_cluster }}"
- host: coming-soon-rss.k8s-prod.h1.int.pdp7.net
diff --git a/personal_infra/puppet/modules/flexisip/manifests/init.pp b/personal_infra/puppet/modules/flexisip/manifests/init.pp
index 9ed962de..a4836329 100644
--- a/personal_infra/puppet/modules/flexisip/manifests/init.pp
+++ b/personal_infra/puppet/modules/flexisip/manifests/init.pp
@@ -4,28 +4,28 @@ class flexisip($flexisip_sdp_port_range_min, $flexisip_sdp_port_range_max, $flex
# keep this repository enabled even if you enable beta/alpha repositories
[Belledonne-stable]
name=Belledonne-stable
- baseurl=http://www.linphone.org/snapshots/$contentdir/$releasever/stable
+ baseurl=https://download.linphone.org/snapshots/$contentdir/$releasever/stable
enabled=1
gpgcheck=0
# enable this if you want post-release patches
[Belledonne-hotfix]
name=Belledonne-hotfix
- baseurl=http://www.linphone.org/snapshots/$contentdir/$releasever/hotfix
+ baseurl=https://download.linphone.org/snapshots/$contentdir/$releasever/hotfix
enabled=1
gpgcheck=0
# enable this if you want next release beta packages
[Belledonne-beta]
name=Belledonne-beta
- baseurl=http://www.linphone.org/snapshots/$contentdir/$releasever/beta
+ baseurl=https://download.linphone.org/snapshots/$contentdir/$releasever/beta
enabled=0
gpgcheck=0
# enable this to have development (unstable) packages
[Belledonne-alpha]
name=Belledonne-alpha
- baseurl=http://www.linphone.org/snapshots/$contentdir/$releasever/alpha
+ baseurl=https://download.linphone.org/snapshots/$contentdir/$releasever/alpha
enabled=0
gpgcheck=0
| EOT
diff --git a/personal_infra/puppet/site/maelcum.mad.int.pdp7.net.pp b/personal_infra/puppet/site/maelcum.mad.int.pdp7.net.pp
index ec1b6300..f69a137f 100644
--- a/personal_infra/puppet/site/maelcum.mad.int.pdp7.net.pp
+++ b/personal_infra/puppet/site/maelcum.mad.int.pdp7.net.pp
@@ -7,7 +7,7 @@ node 'maelcum.mad.int.pdp7.net' {
dhcp-host=d8:8c:79:1a:11:59,chromecast,10.34.10.3
host-record=maelcum.mad.int.pdp7.net,maelcum,10.34.10.2
- dhcp-option=tag:!noroutes,option:classless-static-route,192.168.76.0/24,10.34.10.2,10.43.43.0/24,10.34.10.2,10.17.19.0/24,10.34.10.2
+ dhcp-option=tag:!noroutes,option:classless-static-route,192.168.76.0/24,10.34.10.2,10.43.43.0/24,10.34.10.2,10.17.19.0/24,10.34.10.2,0.0.0.0/0,10.34.10.1
# Kobo
dhcp-host=a4:3c:d7:39:c6:29,set:noroutes
| EOT
diff --git a/personal_infra/puppet/site/nagios.h1.int.pdp7.net.pp b/personal_infra/puppet/site/nagios.h1.int.pdp7.net.pp
index 4d9dd8c6..15eb743d 100644
--- a/personal_infra/puppet/site/nagios.h1.int.pdp7.net.pp
+++ b/personal_infra/puppet/site/nagios.h1.int.pdp7.net.pp
@@ -56,7 +56,7 @@ node 'nagios.h1.int.pdp7.net' {
nagios_command {'check_alex.corcoles.net-gemini-cert':
command_name => 'check_alex.corcoles.net-gemini-cert',
- command_line => '/usr/lib64/nagios/plugins/check_ssl_validity -H alex.corcoles.net -I alex.corcoles.net -p 1965 -c 10 5',
+ command_line => '/usr/lib64/nagios/plugins/check_ssl_validity -H alex.corcoles.net -I alex.corcoles.net -p 1965 -c 10 -w 5',
require => Package['nagios'],
notify => Service['nagios'],
owner => 'nagios',
diff --git a/programming/a_plan_against_the_current_web.md b/programming/a_plan_against_the_current_web.md
deleted file mode 100644
index 55e58da4..00000000
--- a/programming/a_plan_against_the_current_web.md
+++ /dev/null
@@ -1,19 +0,0 @@
-# A plan against the current web
-
-Browsers are controlled by Google, and to a lesser extent, Apple.
-We have arrived to this because browsers have become excessively complex, so that even Microsoft has decided that it's better to be subject to the whims of Google, than to maintain one.
-This complexity is derived from using the web for delivering applications, where its initial purpose was browsing content.
-
-## Part I: Make content websites simple again
-
-See [the content web manifesto](the-content-web-manifesto).
-
-## Part II: Application distribution sucks
-
-We use so many web applications nowadays because there are no alternative platforms to distribute applications with the same reach and convenience.
-
-As a way to start this discussion, let's propose making Flatpak applications work on Windows and macOS, and make them installable and executable from a web link (like Java Web Start or ClickOnce from Microsoft).
-And additionally, let's produce "starter packs" for as many programming languages as possible, so creating these applications is "easy".
-
-Besides all the implementation work, what would be the downsides to this?
-I believe that this would offer better performance than webapps (Flatpak applications on Linux are consistently faster than web apps and Electron apps), and Flatpak apps can already be implemented using many programming languages (webapps are halfway there through WASM, but not there yet).
diff --git a/programming/git/combining_repos_with_josh_filter.md b/programming/git/combining_repos_with_josh_filter.md
deleted file mode 100644
index d63506a3..00000000
--- a/programming/git/combining_repos_with_josh_filter.md
+++ /dev/null
@@ -1,252 +0,0 @@
-# Combining repos with josh-filter
-
-## Introduction
-
-When writing complex software, developers frequently consider incorporating code from other repositories into their repository.
-This choice is controversial and this document will not discuss whether incorporating other repositories is the best option.
-
-Developers have different options to perform this task, including:
-
-* Copying the code
-* Using Git submodules
-* Using Git subtree
-* Using repository "orchestration" tools such as Google's repo
-
-A recent option is Josh.
-Josh is a daemon that can serve virtual Git repositories that apply transformations to repositories.
-With Josh, you can create a virtual Git repository that combines multiple repositories, or a virtual repository that contains a subtree of another repository.
-
-Because Josh is a daemon, using Josh has a greater overhead than other options to combine code from other repositories.
-However, Josh provides the josh-filter command that can be used for similar purposes without the extra maintenance of a service.
-
-This document describes a sample scenario and a step-by-step procedure that you can follow along to learn about josh-filter.
-
-## Scenario
-
-A development team maintains the `foo` repository.
-The code in the `foo` repository uses external code from the `bar` repository.
-The team needs to make changes to code in the `bar` repository, but they have not found a convenient procedure to do so.
-Ideally, the team would like to synchronize their changes with the `bar` repository, so that they can benefit from `bar` updates, and contribute back.
-
-## Preparing the example
-
-Create an `example` directory to contain all the files required for this example.
-
-To follow this example, you will need two repositories standing for the `foo` and `bar` repositories.
-You can use any repository, but the example assumes that the repos are called `foo` and `bar`, and that the `bar` repository will be copied to the `external/bar` path in the `foo` repository.
-
-The example works with two local mirrored repositories in Git, so you can simulate pushing and pulling from a Git provider such as GitHub.
-The example uses `foo.git` and `bar.git` as the URLs of the two repositories.
-You can replace the URLs with real repository URLs, or you can create these repositories by mirroring real repositories.
-If you use local mirrors, then you can also simulate pushing and pulling without affecting real repositories.
-
-To mirror two repositories locally:
-
-```
-git clone --mirror $URL_TO_SOME_REPO foo.git
-git clone --mirror $URL_TO_SOME_REPO bar.git
-```
-
-(The example also assumes that you are using branches named `main` in both repositories.)
-
-You also need the josh-filter tool.
-Follow [the installation instructions](https://josh-project.github.io/josh/reference/cli.html#josh-filter).
-
-## Walkthrough
-
-### Incorporate a repository
-
-Start by cloning the `foo` repository:
-
-```
-git clone foo.git
-```
-
-This command clones the `foo.git` repository to the `foo` directory.
-
-Change to the `foo` clone.
-
-```
-cd foo
-```
-
-Create and switch to a `incorporate-bar` branch.
-
-```
-git switch -c incorporate-bar
-```
-
-Fetch the `main` branch of the `bar` repository.
-The `get fetch` command creates a `FETCH_HEAD` reference that contains the `main` branch.
-This command is a convenient way to work with multiple repositories.
-
-```
-git fetch ../bar.git/ main
-```
-
-Use the `josh-filter` command to incorporate the code in the `FETCH_HEAD` reference to the `external/bar` path.
-This command takes the `HEAD` reference as an input, and filters another reference using a josh filter.
-
-The following command takes the `FETCH_HEAD` reference that contains the `bar` code.
-The `:prefix=external/bar` moves all content of the `FETCH_HEAD` reference to the `external/bar` path.
-The result is stored in a new `FILTERED_HEAD` reference.
-
-```
-josh-filter ':prefix=external/bar' FETCH_HEAD
-```
-
-Merge the `FILTERED_HEAD` reference into your current branch.
-Because the `FILTERED_HEAD` reference contains the `bar` code and is unrelated to the current branch in the `foo` repo, you need the `--allow-unrelated` option.
-
-```
-git merge --allow-unrelated FILTERED_HEAD
-```
-
-After this command, `ls external/bar` and `git log external/bar` show the contents and history of the `bar` repository.
-`git log` and tools such as `gitk` will show the combined history of the two repositories.
-
-Push the branch to the `foo.git` remote.
-
-```
-git push --set-upstream origin incorporate-bar
-```
-
-If you were working with a real repository, then you could create, review, and merge a pull request by following the usual procedures.
-If you are using mirrored repositories, then change to the main branch and merge the `incorporate-bar` branch.
-
-```
-git switch main
-git merge --no-ff incorporate-bar
-git push
-```
-
-(`git merge --no-ff` is equivalent to the "create a merge commit" button in PRs for GitHub.)
-
-At this point, the `main` branch in the `foo.git` repository contains the code from the `main` branch in the `bar.git` repository in the `external/bar` path.
-The code has the full history, and changes can be contributed to and from the `bar.git` repository.
-
-### Incorporating upstream changes from the `bar.git` repository
-
-If new changes are pushed to the `bar.git` repository, then you can pull those changes into the copy in the `foo.git` repository.
-
-#### Simulating changes in the `bar.git` repository
-
-Change to the `example` directory.
-
-Clone the `bar.git` repository.
-
-```
-git clone bar.git
-```
-
-Change to the `bar` directory and make some changes.
-
-Push the changes to `bar.git`.
-
-```
-git push
-```
-
-#### Incorporate the changes
-
-Change to the `example/foo` directory.
-
-Create and switch to a `pull-bar` branch.
-
-```
-git switch -c pull-bar
-```
-
-Fetch the changes again.
-
-```
-git fetch ../bar.git/ main
-```
-
-If you run the `git log FETCH_HEAD` changes, then you can verify that the changes you made are in `FETCH_HEAD`.
-
-Filter, merge, and push the changes again.
-
-```
-josh-filter ':prefix=external/bar' FETCH_HEAD
-git merge --allow-unrelated FILTERED_HEAD
-git push --set-upstream origin pull-bar
-```
-
-After these commands, the `pull-bar` branch contains the new changes from `bar.git`.
-
-If you were working with a real repository, then you could create, review, and merge a pull request by following the usual procedures.
-If you are using mirrored repositories, then change to the main branch and merge the `pull-bar` branch.
-
-```
-git switch main
-git merge --no-ff pull-bar
-git push
-```
-
-### Upstreaming changes in `foo.git` to `bar.git`
-
-If you make changes to the `external/bar` directory in the `foo.git` repository, you can contribute these changes back to the `bar.git` repository.
-
-#### Simulating changes in the `foo.git` repository
-
-Change to the `example` directory and to the `foo` directory.
-
-Make some changes to the `external/bar` directory.
-
-Push the changes to the `foo.git` repository.
-
-```
-git push
-```
-
-### Upstreaming the changes
-
-Change to the `example` directory.
-
-Change to the `bar` directory.
-
-```
-cd bar
-```
-
-Create and switch to a `upstream-from-foo` branch.
-
-```
-git switch -c upstream-from-foo
-```
-
-Fetch the `main` branch from the `foo.git` repository.
-
-```
-git fetch ../foo.git/ main
-```
-
-Apply the opposite filter.
-The `:/external/bar` filter puts the contents of the `external/bar` directory in the root of the repository.
-
-```
-josh-filter ':/external/bar' FETCH_HEAD
-```
-
-```
-git merge --allow-unrelated FILTERED_HEAD
-```
-
-After these commands, the `upstream-from-foo` branch in the `bar.git` repository contains the upstream changes from `foo.git`.
-
-If you were working with a real repository, then you could create, review, and merge a pull request by usual procedures.
-If you are using mirrored repositories, then change to the main branch and merge the `upstream-from-foo` branch.
-
-```
-git switch main
-git merge --no-ff upstream-from-foo
-git push
-```
-
-## Further possibilities
-
-This walkthrough explains how to incorporate code from a repository into a different repository.
-Then, you can synchronize further changes to both repositories.
-
-With similar steps, you can experiment how other Git functionality is affected, such as resolving merge conflicts, or different Git workflows.
diff --git a/programming/git/git_advice.md b/programming/git/git_advice.md
deleted file mode 100644
index 7dd19818..00000000
--- a/programming/git/git_advice.md
+++ /dev/null
@@ -1,22 +0,0 @@
-# Git advice
-
-## Never use `git commit -m`, use `git commit -v`
-
-Configure your system so that `git commit` opens your preferred editor.
-
-With `git commit -v` you can see your commit diff while writing your commit message.
-This helps you review that your commit is correct and write a better commit message.
-
-## Use gitignore properly
-
-See <https://git-scm.com/docs/gitignore>.
-
-Note that by default, Git defaults to `$XDG_CONFIG_HOME/git/ignore` or `$HOME/.config/git/ignore`.
-
-## Use the modern Git commands (or teach them)
-
-Particularly, `git checkout` has many functionalities that now can be handled by more focused commands like `git switch` and `git reset`.
-
-If you have too much muscle memory and are used to them, then consider learning them only to teach other people so that they start with the safer commands.
-
-Many Git commands print suggestions that use the newer commands.
diff --git a/programming/git/github_annoyances.md b/programming/git/github_annoyances.md
deleted file mode 100644
index df373bd7..00000000
--- a/programming/git/github_annoyances.md
+++ /dev/null
@@ -1,9 +0,0 @@
-# GitHub annoyances
-
-## The repository creation wizard can be confusing initially
-
-When creating a new repo, GitHub offers you to populate the repository with some files (a README, a `.gitignore` file, a license).
-
-In some situations, you have an existing directory in your computer with files that you want to be the initial contents of the repo.
-If you create a truly empty repo, then GitHub displays some instructions that can help pushing the contents of your existing directory to the new repo.
-If you use the GitHub features to populate the repo, then GitHub does not display these instructions and uploading your files requires more knowledge.
diff --git a/programming/java/tutorial.md b/programming/java/tutorial.md
deleted file mode 100644
index e4f5f1d1..00000000
--- a/programming/java/tutorial.md
+++ /dev/null
@@ -1,84 +0,0 @@
-# A Java tutorial
-
-This tutorial walks through creating a blank Spring Boot application on Pop!_OS 22.04
-
-## Set up
-
-Open a terminal and run:
-
-```
-$ sudo apt install openjdk-21-jdk code
-```
-
-and follow the prompts.
-
-(You can install both packages from the Pop!_Shop application.
-However, when installing Visual Studio Code, Pop!_Shop defaults to the Flatpak version of the program that is more troublesome than the .deb package.
-Choosing the correct software is easier from the terminal.)
-
-Open Visual Studio Code from "Show Applications".
-
-Follow the "Walkthrough: Setup VS Code", but in the "Rich support for all your languages" step, click "Browse Language Extensions" and install "Extension Pack for Java".
-Do not install a new JDK; you installed one in a previous step.
-
-## Creating a Spring Boot application
-
-In the command palette (ctrl+shift+p), search for and execute "Java: Create Java Project...".
-
-Select "Spring Boot".
-
-Install the Initializr plugin if prompted, you might need to restart the Java project creation wizard.
-
-Select "Maven Project", select the highest Spring version that is not a snapshot.
-
-Select the Java language.
-
-Enter a group id. A group should be a domain in reverse order. The default of "com.example" is OK.
-
-Enter an artifact id. This should be a single keyword. The default of "demo" is OK.
-
-Select the jar packaging type.
-
-Select the Java 21 version matching the JDK you installed in a previous step.
-
-Add only the Spring Web dependency.
-
-When choosing the folder for the project, Visual Studio Code creates a further folder named like the artifact id you enter in a previous step.
-(So do not create a directory with your application name.)
-
-Choose File, Open Folder in the Visual Studio Code menu, then select the directory that the previous step created named like the artifact id.
-
-"Trust the authors".
-
-Navigate to the `src/main/java/com/example/demo/DemoApplication.java" file (the path varies depending on the group id and artifact id).
-
-Right click on the file and select "Run Java".
-If the "Run Java" option is not present, then you might not have "trusted the authors"; in this case, Open Folder again.
-
-Visual Studio Code displays a terminal.
-After a few moments, the terminal displays a message about the application having started.
-
-Open a browser and navigate to <http://localhost:8080>.
-The browser displays an error because the application wizard creates an empty application.
-
-## Advice for people involved in developing the software used in this tutorial
-
-### The Eddy Pop_OS! tool should allow interacting with debconf
-
-When using the Visual Studio Code .deb from Microsoft and not the .deb from Pop!_OS, installing the package from the browser fails.
-The Visual Studio Code .deb has a debconf prompt to add Microsoft package repositories.
-This locks Eddy.
-
-This is likely covered by these issues:
-
-* https://github.com/donadigo/eddy/issues/105
-* https://github.com/donadigo/eddy/issues/107
-
-### The Pop!_Shop should not default to the Visual Studio Code Flatpak
-
-Flatpaks are great most for applications, except for development tools.
-Using the Visual Studio Code Flatpak makes configuring development tools harder.
-
-### Visual Studio Code should not prompt users to download a .tar.gz archive of Java without further instructions
-
-On Linux distributions where the distribution package manager provides a reasonably recent version of Java, users can install Java through the package manager more easily.
diff --git a/programming/on-llms.md b/programming/on-llms.md
deleted file mode 100644
index 744c2a38..00000000
--- a/programming/on-llms.md
+++ /dev/null
@@ -1,123 +0,0 @@
-# On LLMs
-
-This document is mostly an attempt to sort my thoughts on LLMs.
-My intention is to flesh it out over time.
-
-I recently saw a survey about LLMs that had four options: they are great, they are hype, they are harmful, they are fake.
-My thought was: it's all of the above.
-
-## LLMs are awesome
-
-I am absolutely amazed at LLMs.
-By crunching an absurd amount of data, you get a magical box that can continue any dialogue, including dialogue that performs translations, writes code, and many others.
-Although I suspect it's less technologically impressive, you can ask an LLM to draw a picture of anything and the LLM produces it!
-
-The components of LLMs are also magical.
-When machine learning became a trend, many talented people found ways to turn things into vectors, which is amazing.
-With LLMs, this trend continues.
-Word embeddings can represent language as vectors, and we can operate with those vectors with semantic results.
-This is an absolute breakthrough, with many profound implications.
-
-### LLMs seem great for accessibility
-
-Although I will elaborate later on my skepticism about some LLM results, I think that there are already valid applications of LLMs.
-
-Mostly, in accessibility.
-LLMs are effective describing images and videos, can do text to speech and speech to text, and others.
-Accessibility is a very important field and even if I have big objections to LLM use, I think LLMs are likely a net positive for accessibility.
-Any criticism of LLM must take into consideration such applications, and if we want to avoid LLM use in these fields, we *must* provide equivalent alternatives.
-(Some of my criticism below is ethical about LLM companies using content against their authors' wishes, but I think few authors would really object on not-for-profit accessibility usage of LLMs.)
-
-## LLMs are harmful
-
-### LLMs seem to require blatant disregard of intellectual property to be viable
-
-Napster (1999) was not the first high-profile case of massive copyright infringement, but since then, we have frequently seen large companies try to punish individuals to protect their intellectual property.
-
-With LLMs, courts have found out that companies training LLMs have used pirated material in a massive scale.
-
-I can see how for most people this is hugely unfair.
-
-Intellectual property is a complex topic, but morally it does not seem defensible to me that someone can be in trouble for pirating a TV show while a large company can try to make a lot of money through pirating massively.
-
-(As I mentioned, if this is not done for profit, to me this whole problem disappears.)
-
-I have mixed feelings with regard to copyright law, but ultimately I think that authors deserve to have rights over the use of their work.
-For example, I think it is fair that a song composer might forbid an organization such as a political party from using their songs.
-Art ultimately benefits us, and I think some degree of copyright protection helps incentivize creators.
-
-Therefore, given that LLM companies have widely admitted to using copyrighted material without permission, for-profit LLM companies must demonstrate that they have the right to use materials for training, and authors should have an easy way to prevent their works from being used, and to receive fair compensation if they want.
-
-My position does not hinge on how transformative the LLM output is.
-It hinges on what authors want from their work; I am pretty certain that most authors nowadays are OK with other authors taking inspiration from their work (because they were also certainly inspired by other authors), but they are not OK with a large company reselling their work without compensation, or being used to replace authors with a machine.
-
-For other areas, such as writing programs using LLMs, I have similar objections.
-
-LLMs are effective as long as they have sufficient training material.
-In my opinion, if you think the LLM would not be effective if all content from authors who would object to your specific use of an LLM was removed, then you should not use the LLM for that specific purpose.
-
-If you are using an LLM to create an illustration instead of commissioning an illustration, I think you should not do it because I don't think the LLM would be effective if all authors who objected to this use could remove their work from the training set.
-
-(As mentioned, I think very few authors would object to an LLM describing images to a blind person without making an obscene profit.)
-
-#### LLMs might need to be forced to publish all their output
-
-I read somewhere that LLM output should not be copyrightable.
-
-Making all LLM output public would solve a few issues:
-
-* Generated LLM content is now easy to identify by searching into the generated LLM output.
-* IP laundering would result in public material that can be easily replicated and would make commercial benefit difficult.
-
-## LLMs are hype
-
-### LLMs might not be effective for writing code
-
-Hillel Wayne's [What we know we don't know](https://www.hillelwayne.com/talks/ese/ddd/) says that there are very few programming practices that are provably effective in increasing programming productivity.
-
-Apparently, the most effective practice is code review, and its effectiveness is small compared to getting proper sleep, not being stressed, and working the right amount of hours.
-
-Many other practices, such as good variable naming, or static or dynamic typing, do not seem to have a big effect, although most of us believe that some specific practices make us much more productive.
-
-Many people claim that LLMs make them wildly more productive, and I even believe many of them truly believe so.
-
-However, whenever I pull the thread on such claims, I tend to find things that make me skeptic.
-
-I do not rule out that LLMs may increase productivity *today* for *some* tasks.
-But I suspect they might *also* decrease productivity sometimes.
-This lack of certainty of their effectiveness for coding *combined* with the rest of the problems I see with the use of LLMs drive me towards rejecting the use of LLMs for code.
-They might help, but they might also hinder us, and I think it's better to be conservative with regard to their use.
-
-### LLMs cannot be good oracles
-
-My understanding of LLMs is that they are good at producing "things that look like X".
-
-If you ask them to create a picture of X, then they will produce something that looks like a picture of X.
-This is likely what you want, so I think LLMs can be reasonably effective at this task.
-
-If you ask them to answer X, then they will produce some text that looks like the answer to X.
-However, something that reads like the answer to a question is likely *not* an answer to the question.
-
-Although it is surprising that many times the LLM will produce a correct answer, I think ultimately LLMs are not a good way to answer questions because they do that accidentally.
-
-## LLMs are harmful
-
-Or rather, they point to serious problems.
-
-I see many programmers using LLMs to review their code or brainstorm.
-
-Although LLMs might be somewhat effective in those tasks, I am surprised because people do not rely on other people for this.
-
-To me, this points to people not having other people to collaborate with, or preferring to collaborate with a piece of software rather than someone else.
-
-Although this might not be unequivocally harmful, it worries me, and I think we should spend time and resources looking at this phenomenon.
-
-Personally, I think we should offer ourselves to others more, probably in a tit-for-tat fashion.
-
-## Other sources
-
-* [The Future of Software Development is Software Developers](https://codemanship.wordpress.com/2025/11/25/the-future-of-software-development-is-software-developers/)
- > “But this time it’s different, Jason!”
- > [...]
- > And there’s another important distinction: in previous cycles, the technology worked reliably.
- > We really could produce working software faster with VB or with Microsoft Access.
diff --git a/programming/python/about_django.md b/programming/python/about_django.md
deleted file mode 100644
index 37004abc..00000000
--- a/programming/python/about_django.md
+++ /dev/null
@@ -1,141 +0,0 @@
-# About Django
-
-Without more context, one of the technologies I recommend to everyone is Django.
-
-Django is a Python web framework with "batteries included".
-
-Web frameworks can provide more or less tools to write applications.
-Typically, frameworks that provide fewer tools are more flexible and give developers more freedom to develop their applications in the best possible way.
-Similarly, frameworks that provide more tools tend to guide you towards a specific way to writing applications, and typically, require more work if you want to deviate.
-
-In my opinion, many applications you might need to develop are very similar and have similar issues, and solving them ad-hoc for each project is a waste.
-Therefore, I lean towards using frameworks that provide more batteries in most cases.
-
-(Certainly, there are projects that clearly need special approaches, or which deviate enough from any generic web framework.)
-
-In fact, most of the complaints described in this document are caused by Django having too few batteries, not too many!
-
-> [!TIP]
-> [Django training wheels](https://github.com/alexpdp7/django-tws) is my project in alpha to address some of those shortcomings.
-
-## The Django admin
-
-Besides including more batteries than most other frameworks, and being in general a well-engineered framework in my opinion, Django includes the admin.
-
-The admin is a declarative way to build administrative sites where some users edit data stored in the application database.
-
-Many similar tools exist, but I have not found any other tool that can do so much.
-
-* The Django admin handles multi-table relationships very well, including picking foreign key targets and editing related table data.
- For example, if a person entity has a "parent" related foreign key relationship, the Django admin provides a search functionality to pick a person's parent.
- If the person entity has a list of children, the Django admin provides a way to add and edit children from the person form.
-
-* The Django admin has a simple, but useful for many scenarios permissions functionality, where editing certain entities is restricted to groups of users.
-
-The Django admin is frequently a big boost during the early development of database-backed applications, and sometimes it can provide value during a big part of the life of an application.
-
-Additionally, traditionally when working with frameworks without an equivalent facility, the friction of adding an interface to edit a piece of data can be large.
-Developers pressed for time might opt to hardcode the data in the source code of the application, requiring code changes to modify certain behaviors of the application.
-When the friction to add a user interface to edit such data is low, developers can configure the admin to let those users edit the data directly without going through the developers.
-
-## Django problems
-
-However, there are still many common issues for which batteries could exist, but that Django does not provide.
-
-### Django has no support or documentation about packaging Django projects
-
-Most Django projects have dependencies besides Django.
-In order to develop and deploy Django applications, you likely must install other dependencies.
-Django does not include documentation nor support to do this.
-
-Many different approaches and tools exist to manage Python project dependencies.
-Understandably, endorsing one particular approach in Django could be controversial.
-So Django leaves the choice of approach up to users.
-Additionally, Django adds a few difficulties in Python project management, and users must figure out how to handle Django projects in their chosen approach.
-
-Several initiatives have tried to tackle this problem, notably:
-
-* https://github.com/radiac/nanodjango
-
-### Django settings are a partial solution
-
-Django provides settings to manage the configuration for a Django project.
-You implement Django settings by writing a Python module.
-
-For example, the default Django template includes the following snippet to configure the database connection:
-
-```
-DATABASES = {
- 'default': {
- 'ENGINE': 'django.db.backends.sqlite3',
- 'NAME': BASE_DIR / 'db.sqlite3',
- }
-}
-```
-
-Besides assigning a setting directly like in the preceding snippet, you can use Python code to assign settings.
-
-This allows you to tackle many common issues, such as setting up a different database connection for development and production, while keeping the production database credentials away from the source code repository.
-There are many similar issues that you must tackle in nearly all projects.
-
-Several initiatives tackle some of those issues:
-
-* https://github.com/jazzband/dj-database-url provides a way to configure the database connection through an environment variable.
-
-### Django does not explain a development database workflow
-
-Django provides migrations to handle schema changes.
-Migrations work well and are a valid solution to handle schema changes in production.
-
-However, while developing a Django application, you frequently need to make many temporary changes to the data definition until you find the right data definition.
-
-In my opinion, if you follow the Django documentation, then you might end up using migrations for those development schema changes.
-This is awkward and problematic, and there are procedures to develop database changes that work better.
-
-I would like a command that recreates your database, applying unmigrated model changes.
-This command could also have hooks to load sample data.
-(Likely, Python code and not fixtures.)
-
-### Django only tackles database-based, server-side-rendered, non highly interactive web applications
-
-While certainly a huge amount of applications:
-
-* Revolve around data stored in a relational database
-* Are better implemented as server-side rendering applications
-* Do not require very complex or real-time interactions
-
-There are certainly many applications that do not fit this mold.
-
-In my opinion, focusing on database-based applications is a good decision.
-Many Django features (like the admin) revolve around the database, and a framework oriented to other applications likely should be very different.
-
-However, more and more applications break the limits of server-side rendering, and while you can build such applications with Django, you need a lot of effort or finding additional libraries to use.
-
-For example:
-
-* [Django REST framework](https://www.django-rest-framework.org/) provides a layer to provide REST APIs on top of the Django ORM.
-* Projects exist to add support for Django for front end frameworks such as [htmx](https://htmx.org/) or [Hotwire](https://hotwired.dev/).
- These frameworks are an intermediate step between traditional server-side-rendered applications and JavaScript front ends, enabling most of the benefits of JavaScript front ends within the traditional server-side rendering approach.
-
-Additionally, providing an API is also useful beyond JavaScript front ends.
-APIs are necessary for other purposes, such as implementing mobile apps to interact with your application, or just providing an API for programmatic access to your application.
-
-### Some common tasks should have more tutorial content
-
-The Django documentation is mostly for reference, covering all Django features, but with little content on how to use Django.
-The items I list below likely are documented on books, websites, forums, etc.
-If you know a good source for many of those, even if it is paid, feel free to let me know to add references.
-
-* Admin
- * Restricting users to a subset of the instances of a model.
- For example, users belong to organizations and users should only see instances of some model related to their organization.
- The FAQ contains [How do I limit admin access so that objects can only be edited by the users who created them?](https://docs.djangoproject.com/en/5.1/faq/admin/#how-do-i-limit-admin-access-so-that-objects-can-only-be-edited-by-the-users-who-created-them), which is a very similar question and points to the features you need to use to achieve these goals.
- (These requirements are often related to requiring [extending the existing User model](https://docs.djangoproject.com/en/5.1/topics/auth/customizing/#extending-the-existing-user-model).)
- * Having a search UI for reference fields instead of dropdowns.
- Many projects similar to the admin only offer dropdowns for reference fields.
- This does not work when the referenced objects are more than a couple.
- Django calls this [`raw_id_fields`](https://docs.djangoproject.com/en/5.1/ref/contrib/admin/#django.contrib.admin.ModelAdmin.raw_id_fields), and it is difficult to learn that this feature exists.
-
-## Further reading
-
-* [CRUD is an important unsolved problem](../crud_is_an_important_unsolved_problem.md)
diff --git a/programming/python/creating_nice_python_cli_tools.md b/programming/python/creating_nice_python_cli_tools.md
deleted file mode 100644
index b192da1a..00000000
--- a/programming/python/creating_nice_python_cli_tools.md
+++ /dev/null
@@ -1,40 +0,0 @@
-Following this advice can make your tools easy to install by others, pleasant to use, robust, cross-platform, and powerful.
-
-* Use [my suggestions for setting up Python projects](project_setup.md), particularly:
- * Provide instructions for installing your tool using [pipx](https://github.com/pypa/pipx).
- Using pipx, people can install and upgrade your script using a simple command that requires no administrative privileges (but it requires having Python and pipx installed).
- * As you are using [uv](https://docs.astral.sh/uv/), following the indications above:
- * Use [entry points](https://docs.astral.sh/uv/concepts/projects/config/#entry-points), so when installing your tool via pipx or other means, your scripts are added to the user's path.
- * Dependencies you define will be installed automatically along with your application.
- This reduces the effort users need to use your application if you need third-party libraries.
- However, I would still advise to avoid unnecessary dependencies (for simple HTTP requests you can use the base library. If you do complex requests, then using a third-party library might be much simpler).
- As you are using pipx, those dependencies will be installed to a isolated virtualenv, so they will not interfere with anything on your system.
- * As your application is properly packaged, you can split your code into different Python files and use imports without issues.
-* If your application requires secrets, such as credentials or others, consider using:
- * The standard [getpass](https://docs.python.org/3/library/getpass.html) module.
- This prompts for a string on the command line, hiding what the user types.
- * The [keyring](https://pypi.org/project/keyring/) library.
- This stores secrets using your operating system facilities.
-* Use the [appdirs](https://pypi.org/project/appdirs/) library to obtain "user paths", such as the users directory for configuration, cache, or data.
- appdirs knows the proper paths for Linux, macOS and Windows.
- So for example, if your tool caches files and uses appdirs to find the cache directory, you might gain benefits such as cache files being excluded from backups.
-* If your tool requires significant time to complete a process:
- * Use the [tqdm](https://tqdm.github.io/) library to add a progress bar.
- * But also consider using the standard [concurrent.futures](https://docs.python.org/3/library/concurrent.futures.html) module to add parallelism if you can.
- The [map](https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.Executor.map) function is particularly easy to use.
- Use it with a [ThreadPoolExecutor](https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.ThreadPoolExecutor) if the parallel tasks are IO-bound or invoke other programs, or with [ProcessPoolExecutor](https://docs.python.org/3/library/concurrent.futures.html#processpoolexecutor) if they perform significant CPU work in Python (to avoid the [GIL](https://wiki.python.org/moin/GlobalInterpreterLock)).
- * Consider using the standard [logging](https://docs.python.org/3/library/logging.html) module with a format that uses a timestamp, so users can inspect how much time is spent in different parts of the program.
- You can also use logging module to implement flags such as `--debug` and `--verbose`.
-* Although fancier tools exist, the standard [argparse](https://docs.python.org/3/library/argparse.html) module is good enough for most argument parsing.
- It has decent support for [sub-commands](https://docs.python.org/3/library/argparse.html#sub-commands), and the linked document describes a very nice pattern to define functions for sub-commands, under "One particularly effective way of handling sub-commands..."
- Provide help text for non-obvious parameters.
- argparse supports a lot of different argument types with a lot of functionality out of the box, such as enumerated options, integers, and file names.
- The main reason for using a fancier argument parsing is that argparse does not have autocomplete support, but you can add [argcomplete](https://github.com/kislyuk/argcomplete) to an argparse program with minimal modifications to retrofit autocomplete.
-* Remember that the standard [json](https://docs.python.org/3/library/json.html) module is built-in.
- You can use it to add a mode to your tool that generates JSON output instead of human-readable output, for easy automation of your tool, maybe using [jq](https://stedolan.github.io/jq/) or [fx](https://github.com/antonmedv/fx).
-* Use the standard [subprocess](https://docs.python.org/3/library/subprocess.html) module to execute other commands.
- * Remember never to use `shell=True`, so among other things, your tool will work correctly with files using spaces in their names.
- * Use `check=True` so if the subprocess fails, an exception will be raised.
- This is likely the best default behavior, although the error is a bit ugly, this normally prevents ugly problems and it's a safe option.
-
-You can find examples for many of those techniques in my [repos](https://github.com/alexpdp7?tab=repositories&q=&type=&language=python&sort=).
diff --git a/programming/python/dependency_handling.md b/programming/python/dependency_handling.md
deleted file mode 100644
index 8acf0cee..00000000
--- a/programming/python/dependency_handling.md
+++ /dev/null
@@ -1,111 +0,0 @@
-# Some brief notes about Python dependency management
-
-This article is mostly written for people who have already used Setuptools and have faced issues derived from its "limitations".
-Specifically, if you have seen files named `requirements.txt` and have wondered how they work, what problem do they solve, and if they are something you should investigate, I hope you find this article interesting.
-
-If you are starting to write Python software and you are looking at an introductory text about distributing your software and using dependencies, I would recommend you to skip directly to using the "new generation" Python packaging tools.
-This way, you can avoid most of the complexities in this post.
-You can also check out the [Python Packaging User Guide](https://packaging.python.org/en/latest/) and [my own prescriptive project setup recommendations](project_setup.md).
-
-Most programs can use third-party libraries to implement parts of their functionality without implementing everything from scratch.
-
-pip is the recommended package installer for Python.
-Python installers include pip, although pip is a component that can be installed separately from Python.
-Some Linux distributions separate pip from the main Python package (for example, Debian has a `python3` package and a `python3-pip` package), but a Python install without `pip` is not really fully functional for many purposes.
-
-pip fetches Python packages from diverse sources and adds them to a Python installation.
-Python packages can specify other packages as dependencies, so when pip installs a package, it also installs the required dependency chain.
-
-The traditional mechanism for packages to specify dependencies is Setuptools and other closely related projects.
-
-## About Setuptools
-
-Setuptools is a build and distribution system based on the distutils module that was part of the base Python library.
-
-Package metadata in Setuptools can be defined in many different ways, such as a `setup.py` file, a `setup.cfg` file, or a `pyproject.toml` file.
-In these files, you list the dependencies for your package, specifying the name of the package and constraints.
-
-Constraints define which version of a dependency you want to use.
-The constraint does not be an exact version, it can also be a range of versions, or a constraint such as "lower than version n".
-
-(Constraints additionally can specify other restrictions, such as requiring different versions for different Python versions, and other interesting possibilities.)
-
-In my opinion, although you can package applications and libraries properly using Setuptools, doing it correctly requires much knowledge, effort, and is error-prone.
-
-## Version locking and `requirements.txt`
-
-There is a dependency-management approach that can be very effective in many cases.
-
-This approach involves differentiating between "applications" and "libraries".
-
-Libraries are Python packages meant to be used as a dependency by other Python code.
-Applications are Python code that may use other libraries as dependencies, but which no other Python code depends on.
-
-### Specifying dependencies for libraries
-
-Libraries specify coarse but safe dependency requirements.
-
-Suppose that we are developing the foo library.
-The foo library depends on the bar library.
-The bar library uses a versioning scheme similar to semantic versioning.
-When we develop the foo library, we use version 1.2.3 of the bar library.
-
-Then, we specify that the foo library depends on the bar library, with a version constraint like `>=1.2.3, <1.3`.
-This version constraint lets the library to be used with the 1.2.4 version, which is likely compatible with the code in the foo library, and even introduce valuable bug fixes.
-However, the 1.3.0 version of the bar library would not be a valid dependency.
-This is probably a good idea; the 1.3.0 may contain changes that the foo code is incompatible with.
-(When we later create new versions of the foo library, we may want to consider depending on newer versions of the bar library, and possibly update the code so it continues working correctly.)
-
-This helps reduce conflicts.
-As libraries specify coarse dependencies, the chances of two libraries having incompatible requirements is lower.
-However, specifying coarse dependencies probably requires more testing to ensure that if different dependency versions are installed, the library works correctly.
-
-### Specifying dependencies for applications
-
-Applications specify exact dependency requirements.
-
-While libraries are not usually run on their own, applications are executed directly by end users.
-If a library does not work well, then you can temporarily go back to an older version or apply other fixes.
-But if an application does not work correctly, you have worse problems.
-
-If you specify exact dependency versions for an application, users of the application will always use a single combination of dependencies, which makes making things robust easy.
-
-A popular approach is for applications to specify Setuptools requirements with coarse versioning (just like libraries do), but to provide a list of the specific versions used for development and deployment.
-To create this list of dependencies, you can install your application using pip or some other mechanism, then extract a list of the dependency versions that were installed and store it in a file.
-For example, you can do this by executing:
-
-```
-$ pip install . # executed from the root of the application source code
-$ pip freeze >requirements.txt
-```
-
-Later on, if you install the application using the following command:
-
-```
-$ pip install -r requirements.txt
-```
-
-Then you will always install the same set of dependencies, preventing issues by updated dependencies.
-
-Note: pip and other package installers do *not* use `requirements.txt` or any other similar file outside the `setup.cfg` file and the other files defined in Setuptools.
-If you do not install your application explicitly using `pip install -r requirements.txt`, you will probably install a different set of dependencies.
-
-## Beyond version locking
-
-Following the approach above can be enough to use dependencies correctly.
-
-However, maintaining the Setuptools version dependencies and `requirements.txt` is straightforward, but tedious.
-Also, this approach of dependency management is not obvious, and may not be easy to get right completely.
-
-For these reasons, several projects have appeared that implement approaches similar to the one described above, but more automatic and prescriptive.
-These projects often manage automatically a file equivalent to `requirements.txt`, while the developer only specifies coarse dependencies for applications.
-
-Some of these tools are listed by [a page about relevant projects about packaging](https://packaging.python.org/en/latest/key_projects/) maintained by the [Python Packaging Authority](https://www.pypa.io/).
-Look for tools about managing dependencies and packaging.
-
-Thanks to some improvements in the Python ecosystem, pip can nowadays install dependencies using many different packaging tools correctly.
-
-These projects can also offer some other improvements, so I would encourage Python developers to investigate them and try them out.
-
-However, also note that following a correct approach, Setuptools and manual version locking are perfectly valid ways to manage Python code dependencies.
-Also, there are projects such as [pip-tools](https://github.com/jazzband/pip-tools) that complement Setuptools, addressing many of the issues described here, without requiring entirely new packaging tools.
diff --git a/programming/python/project_setup.md b/programming/python/project_setup.md
deleted file mode 100644
index a5f0c789..00000000
--- a/programming/python/project_setup.md
+++ /dev/null
@@ -1,114 +0,0 @@
-There is a significant amount of Python project tooling. This document collects my personal recommendations on how to set up a Python project.
-
-It is not meant to reflect the best or most common practices, just my personal taste.
-
-# Use pipx
-
-Pipx is a tool that installs Python packages to your user environment. It creates an isolated environment for every tool, so if you install multiple packages they won't have version conflicts. It also takes care of adding a module's entrypoints to your user path.
-
-uv can do very much the same and additionally uv can install most Python versions.
-However, at the time of writing this, pipx is available as a package in many Linux distributions, while uv is not.
-
-If your project can be packaged so that it works with pipx, then many Linux users will be able to install it with pipx after installing pipx with their package manager.
-
-uv can be more convenient for software that requires specific versions of Python that are not available in Linux distributions, but in general cannot be installed with Linux package managers.
-
-# Use uv
-
-When using third-party dependencies in your Python code, it is highly interesting to avoid installing any project-specific dependency outside the project.
-
-To achieve that, traditionally virtualenvs are used; those are miniature Python installations where you can install any library you want. Virtualenvs need to be explicitly activated to be used, so it is easy to have a virtualenv for each Python project you are working on.
-
-uv is a tool that leverages virtualenvs to manage a project's dependencies, managing virtualenvs automatically.
-uv can also manage Python distributions, downloading automatically Python versions other than the existing ones on your system.
-
-There are many similar tools such as pipenv and there are many multiple ways to specify a project's dependencies (`setup.py`, `requirements.txt`, etc.); uv provides a convenient way to do everything.
-
-Consider reading [some brief notes about Python dependency management](dependency_handling.md).
-
-# Test your code
-
-Write the necessary amount of tests so you can make changes to your code with confidence.
-
-If you find yourself iterating over a piece of code slowly, try to isolate the code you are writing so it can be tested in isolation for faster iteration.
-
-## Use pytest for testing
-
-Python provides *two* testing frameworks in its standard library, but they have some limitations:
-
-* `unittest` is an xUnit-style testing framework which follows non-PEP-8 naming conventions (probably because it copied the Java's jUnit), so extra work needs to be done to make your test cases PEP-8 compliant
-* `doctest` is a tool which allows you to run tests embedded in comments. For some code, it is great and helps you provide good, up-to-date documentation. However, a significant amount of code is awkward to test using `doctest`.
-
-Use `doctest` whenever you can, but outside that, use `pytest` to write PEP-8-compliant tests.
-
-Ensure that your test suite runs correctly by running `pytest` without any arguments.
-
-Use plain Python's `assert` statements to check assertions in your tests; `pytest` does some magic to provide nice error messages on failed assertions.
-
-## Gate your changes with testing
-
-Set up your version control so changes cannot be made to your main codeline without passing continuous integration tests (and possibly, code review).
-
-# Perform automated code formatting and static checking
-
-> [!NOTE]
-> I have been using [ruff](https://github.com/astral-sh/ruff) recently.
-> Not enough to recommend it unconditionally over flake8/black, but I am liking it so far.
-> Consider testing it.
-> It requires slightly less configuration and it comes with more lints.
-
-## Use Ruff
-
-Use Ruff to format and lint your code.
-
-# Version control
-
-## Use a minimal gitignore file
-
-See [use gitignore properly](../git/git_advice.md#use-gitignore-properly).
-
-## Keep your code together
-
-All the code you modify as part of the project should be kept in a single repository so you can make atomic changes. If you find yourself making changes across multiple repositories and having to coordinate them, consider merging those repositories.
-
-Use git submodules or similar mechanisms to refer to code you modify that must be kept external.
-
-Use [Josh](../git/combining_repos_with_josh_filter.md) to publish parts of the repository outside the main repository if needed.
-
-# Support multiple modern versions of Python
-
-Unless you have a specific requirement to support Python 2, don't.
-
-It is reasonable to support multiple versions of Python 3 from 3.4 onwards. Supporting the oldest versions might limit the features you can use (although features from more modern versions have been backported), so evaluate which operating systems and versions you need to support and try to support Python versions readily available for them (in Linux, by using mainline distro repos, for instance).
-
-Even if you are not running your code using the latest versions of Python, try to support all the newest available versions.
-
-Use continuous integration to run your tests in all supported versions of Python.
-
-# Use ipython and ipdb
-
-Add ipython and ipdb as development dependencies.
-
-# Versioning
-
-Unless you have a specific requirement to support multiple versions of your code or to distribute to a platform that *requires* versioning (such as pypi), do not explicitly version your code but allow implicit versioning (e.g. it should be possible to identify which Git commit deployed code comes from).
-
-# Documentation
-
-Provide a `README` containing:
-
-* The purpose of the code
-* How to use the code
-* How to develop the code
-
-If the `README` becomes unwieldly, separate usage instructions to `USAGE` and/or development instructions to `HACKING`.
-
-Provide docstrings detailing the external interface of Python modules. Provide internal comments in modules detailing implementation.
-
-If you are developing a library/framework, consider using Sphinx. Sphinx can create a documentation website for a Python project, taking advantage of docstrings.
-
-# Distribution
-
-If your code can be executed from a command line, consider documenting installation via `pipx`.
-
-If your code has dependencies that are not trivial to install (such as Pandas), consider publishing a Docker image or using dependencies that are simpler to install. Design your Docker images so rebuilding the image on most changes is fast.
diff --git a/programming/python/python_modules_primer.md b/programming/python/python_modules_primer.md
deleted file mode 100644
index d80b96d3..00000000
--- a/programming/python/python_modules_primer.md
+++ /dev/null
@@ -1,272 +0,0 @@
-# Python Modules Primer
-
-## Prerequisites
-
-These instructions assume a Linux environment.
-A macOS environment is similar, but not identical.
-A Windows environment is more different.
-
-## Previous knowledge
-
-### A refresher on the `PATH` variable
-
-If you execute the following command in your terminal:
-
-```
-$ echo hello
-```
-
-, the shell searches for the `echo` command in the directories listed in your `PATH` environment variable.
-You can display your `PATH` variable by running:
-
-```
-$ echo $PATH
-/home/user/.local/bin:/home/user/bin:/usr/share/Modules/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin
-```
-
-The contents of the `PATH` variable depend on your particular environment.
-
-If you run the following command:
-
-```
-$ which echo
-/usr/bin/echo
-```
-
-The `which` command prints where the shell locates the `echo` command.
-
-### A refresher on shell scripts
-
-If you create a file named `foo.sh` with the following contents:
-
-```
-#!/bin/sh
-
-echo hello
-```
-
-You define a "shell script".
-The first line indicates that this shell script is executed by using the `/bin/sh` command.
-The rest of the file are commands to be executed by the shell command.
-These commands behave as if you typed them into your terminal, so if you execute this script, the command `echo hello` will be executed, printing `hello`.
-
-If you try to run `foo.sh` like you run the `echo` command, by typing its name, it does not work:
-
-```
-$ foo.sh
-bash: foo.sh: command not found...
-```
-
-, because the shell looks for the `foo.sh` in the directories listed in the `PATH` variable.
-Unless you created the `foo.sh` file in a directory like `/usr/bin`, the shell will not find the `foo.sh` command.
-
-A solution to this problem is to specify the path to the `foo.sh` file, instead of relying on the `PATH` variable.
-However, if you do this, you face a second problem.
-
-```
-$ ./foo.sh
-bash: ./foo.sh: Permission denied
-```
-
-This happens because only files with the executable permission can be executed in this way.
-To solve this, add the executable permission; then it works:
-
-```
-$ chmod +x foo.sh
-$ ./foo.sh
-hello
-```
-
-## The `import` statement in Python
-
-### Importing from the Python standard library
-
-Run the following commands by using the Python REPL:
-
-```
-$ python3
-Python 3.9.17 (main, Aug 9 2023, 00:00:00)
-[GCC 11.4.1 20230605 (Red Hat 11.4.1-2)] on linux
-Type "help", "copyright", "credits" or "license" for more information.
->>> import datetime
->>> datetime.datetime.now()
-datetime.datetime(2023, 9, 11, 21, 53, 16, 331236)
-```
-
-`import` works in a similar way to running a command in the shell.
-Python searches a number of directories looking for the `datetime` module.
-
-To see which directories are searched, run:
-
-```
-$ python3
->>> import sys
->>> sys.path
-['', '/usr/lib64/python39.zip', '/usr/lib64/python3.9', '/usr/lib64/python3.9/lib-dynload', '/home/alex/.local/lib/python3.9/site-packages', '/usr/lib64/python3.9/site-packages', '/usr/lib/python3.9/site-packages']
-```
-
-`sys.path` is a list of the directories that the `import` command searches.
-The contents of `sys.path` depend on your operating system and Python installation method.
-
-In my system, the `/usr/lib64/python3.9` directory contains the `datetime.py` module.
-
-```
-$ head /usr/lib64/python3.9/datetime.py
-"""Concrete date/time and related types.
-
-See http://www.iana.org/time-zones/repository/tz-link.html for
-time zone and DST data sources.
-"""
-
-__all__ = ("date", "datetime", "time", "timedelta", "timezone", "tzinfo",
- "MINYEAR", "MAXYEAR")
-...
-```
-
-`/usr/lib64/python3.9` contains the modules in [the Python standard library](https://docs.python.org/3/library/).
-
-### Importing your Python files
-
-If you create a file with the `a.py` name:
-
-```
-def f():
- return 2
-```
-
-, and another with the `b.py` name:
-
-```
-import a
-
-print(a.f())
-```
-
-, then:
-
-```
-$ python b.py
-2
-```
-
-
-This works, because `sys.path` contains `''`, which means "the current directory".
-
-(`sys.path` is very similar to the `PATH` variable. However, `sys.path` contains the current directory by default, whereas `PATH` does not.)
-
-When `import a` is executed, then Python searches the directories in `sys.path` for an `a.py` file; it is found when checking the `''` path.
-When `import datetime` is executed, Python searches in the current directory (because `''` comes first in the path), doesn't find it, but then finds it in the following `/usr/lib64/python3.9` directory.
-Python iterates over the `sys.path` directories, and loads the *first* matching file.
-
-## Installing libraries
-
-When writing Python software, sometimes it is enough with the modules included in the standard library.
-However, frequently you want to use other libraries.
-To use Python libraries, you must install them using the `pip` program.
-
-The `pip` program is not part of the `python3` package in some Linux distributions, and comes from the `python3-pip` package.
-
-The `pip` program can download libraries from https://pypi.org/ , the Python package index, and install them.
-`pip` installs libraries to a "Python environment".
-
-Old versions of `pip` defaulted to installing libraries to the "system" Python environment.
-In a Linux system, the system Python environment is located in a directory such as `/usr/lib64/python3.9`.
-By default, normal Linux users cannot write to `/usr`, so installing a package would fail.
-
-Modern versions of `pip` detect that they cannot write to the "system" Python environment, and then redirect the install to the "user" Python environment.
-The "user" Python environment is in a directory such as `~/.local/lib/python3.9`.
-
-You could use a command such as `sudo pip install` to grant `pip` the privileges required to write to `/usr`.
-However, this can make a Linux system unusable.
-Most Linux systems use software that uses the "system" Python environment.
-Altering the "system" Python environment can break such software.
-Do not run `sudo pip install` with root privileges unless you know why you need this.
-
-If you use a modern `pip` (or use the `--user` option), you can install libraries to the "user" Python environment.
-However, this is problematic because a Python environment can only contain a single version of a Python library.
-If you have two different Python programs that different versions of the same library, then these two programs cannot coexist in the "user" Python environment.
-
-In general, Python virtual environments are used to address this problem.
-
-## Creating Python virtual environments
-
-If you run:
-
-```
-$ python3 -m venv <some path>
-```
-
-This will create a directory with the path you specify, with the following contents:
-
-```
-<some path>
-├── bin
-│   ├── activate
-│   ├── pip
-│   ├── python
-├── include
-├── lib
-│   └── python3.9
-```
-
-The `python` and `pip` commands are copies of the same commands from the "system" Python environment.
-
-But these commands work differently from the "system" Python environment commands:
-
-```
-$ <some path>/bin/python
->>> import sys
->>> sys.path
-['', '/usr/lib64/python39.zip', '/usr/lib64/python3.9', '/usr/lib64/python3.9/lib-dynload', '<some path>/lib64/python3.9/site-packages', '<some path>/lib/python3.9/site-packages']
-```
-
-`sys.path` uses the `lib` directories in the virtual environment.
-
-When you use the `pip` program from the virtual environment, it installs the libraries to the virtual environment.
-
-You can create as many virtual environments as you need, and you can install different versions of libraries to each virtual environment.
-
-## Activating Python environments
-
-You can run the `python` and `pip` commands by specifying the full path, like we did when executing the `foo.sh` command earlier.
-
-By default, if you run `python`, the shell will invoke the `python` command from the "system" Python environment because it is in a directory included in the `PATH` variable.
-If you specify the full path, you override this.
-
-To save typing, the `bin` directory of a virtual environment contains an `activate` file.
-The `activate` file is a "special" shell script that must be invoked in one of the following two ways:
-
-```
-$ source <some path>/bin/activate
-```
-
-```
-$ . <some path>/bin/activate
-```
-
-`source` and `.` are synonyms.
-They are special shell commands that are needed for the `activate` command to work correctly.
-
-`activate` alters your path, so that the `bin` directory in your virtual environment comes first in your path.
-
-```
-$ echo $PATH
-/home/user/.local/bin:/home/user/bin:/usr/share/Modules/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin
-$ . <some path>/bin/activate
-(some path) $ echo $PATH
-<some path>/bin:/home/user/.local/bin:/home/user/bin:/usr/share/Modules/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin
-```
-
-, and thus if you run `python`, `<some path>/bin/python` will be executed instead of `/usr/bin/python`.
-
-Besides changing your prompt to indicate the virtual environment is activated, `activate` only alters your `PATH`.
-`activate` is not mandatory to use a virtual environment.
-For example, when running the Python command, if you specify the path of the Python executable in a virtual environment, the command will execute as if the virtual environment had been activated.
-Tools such as `poetry` have commands such as `poetry run` that can run commands inside a virtual environment without activating it.
-Activation can save time, but it is also more error-prone than more explicit means of using virtual environments.
-
-## Further reading
-
-* [Some brief notes about Python dependency management](dependency_handling.md) continues this explanation, introducing the need for packaging tools.
-* [Installing Python Modules](https://docs.python.org/3/installing/index.html), from the official Python documentation, describes the `pip` program in more depth.
-* [`venv` - Creation of virtual environments](https://docs.python.org/3/library/venv.html), from the official Python documentation, describes virtual environments in more depth.
diff --git a/programming/python/scraping_with_selenium_on_docker.md b/programming/python/scraping_with_selenium_on_docker.md
deleted file mode 100644
index 61ba1c12..00000000
--- a/programming/python/scraping_with_selenium_on_docker.md
+++ /dev/null
@@ -1,7 +0,0 @@
-Don't use Selenium, use [Playwright](https://playwright.dev/python/):
-
-* Playwright automatically sets up headless browsers.
-* Provides convenient abstractions for locating elements in a page (mostly no XPath required. It can match "intelligently" using text).
-* Has a handy UI tool that records your actions in a browser and writes equivalent *readable* Playwright code.
-
-Further reading: https://new.pythonforengineers.com/blog/web-automation-dont-use-selenium-use-playwright/
diff --git a/programming/the-content-web-manifesto/NOTES.org b/programming/the-content-web-manifesto/NOTES.org
deleted file mode 100644
index b56c4bf1..00000000
--- a/programming/the-content-web-manifesto/NOTES.org
+++ /dev/null
@@ -1,30 +0,0 @@
-* [[README.md][README]]
-
-- https://www.fixbrowser.org/
-
-** Document how terminal browsers can invoke a full browser to execute JavaScript
-
-See [[https://www.gnu.org/software/emacs/manual/html_node/eww/Advanced.html]], w3m has similar stuff.
-
-Also:
-
-- https://github.com/abhinavsingh/proxy.py Extensible Python proxy
-- https://github.com/TempoWorks/txtdot
-- https://github.com/4383/photonos
-- https://sr.ht/%7Ebptato/chawan/
-- https://offpunk.net/
-
-Browsers as a platform to manage content:
-
-- Just view the content as HTML with user-defined styling
-- Archive all that we see so that we can locate content we have read easily, share with others, etc.
-- RSS/Gemfeed/content subscription
-
-** Annotate URLs with another URLs
-
-- For example, add transcriptions to comic strips that do not have them
-- The server pushes serialized bloom filters of annotated URLs (or entire annotation sets?) so that clients do not have to leak what they are browsing.
-- Maybe https://dokie.li/
-- Alternative approach with Violentmonkey for accessibility purposes: [[https://github.com/alexpdp7/aelevenymonkey]].
-
-** NoScript configuration merge
diff --git a/programming/the-content-web-manifesto/README.md b/programming/the-content-web-manifesto/README.md
deleted file mode 100644
index d05256b9..00000000
--- a/programming/the-content-web-manifesto/README.md
+++ /dev/null
@@ -1,52 +0,0 @@
-# The content web manifesto
-
-These are my recommendations for creating "content" websites.
-In a content website visitors mostly read content.
-Some example content websites are Wikipedia, news websites, and blogs.
-
-Also see [further notes](NOTES.org).
-
-## General guidelines
-
-### Test your website with a terminal browser without JavaScript like w3m, lynx, or elinks
-
-If your website is usable with one of those browsers, then:
-
-* Your website does not require JavaScript to load.
- This automatically addresses most annoyances with content websites.
- Websites that do not require JavaScript tend to require less resources, making them faster and lighter.
-
-* Your website does not rely on non-text content.
- Text content is uniquely flexible, it is frequently the most amenable media to being processed by the following systems and processes:
-
- * Text-to-speech systems
- * Translation (both human and automatic)
- * Edition (making changes to text content)
- * Quoting/embedding (readers can copy parts of your text to cite or promote your content)
-
- Images, audio, video or other interactive media might be required to convey the message of your content.
- Therefore, the content web manifesto does not forbid their use.
- However, non-text content should always be accompanied by at least a text description of the content, and ideally, an alternate text version of the content.
-
-* Your website will work with user styling.
- Providing a visual style via CSS and others is fine, but users should be able to read your content with *their* choice of font, text size, color, and others.
- This is important for accessibility, but also for everyone's comfort.
-
-And more importantly, this weakens browser monopolies controlling the web.
-Not even massive companies like Microsoft dare to maintain a browser engine, leaving the web subject to the power of the very few browser vendors in existence.
-But if your web content can be read under a terminal browser without Javascript, then your content is automatically accessible by a massive amount of browsers, including very simple ones.
-
-(Alternatively, use [the Gemini protocol](https://geminiprotocol.net/).)
-
-### Provide granular URLs
-
-When providing a significant amount of content, make sure readers can link to specific content of interest.
-
-This can be achieved by:
-
-* Splitting your content in different pages
-* Providing HTML headers with anchors
-
-### Date content
-
-Always make initial publication and edition dates available.
diff --git a/programming/we_cant_code_good_so_take_llm_hype_with_a_grain_of_salt.md b/programming/we_cant_code_good_so_take_llm_hype_with_a_grain_of_salt.md
deleted file mode 100644
index 675f8a2e..00000000
--- a/programming/we_cant_code_good_so_take_llm_hype_with_a_grain_of_salt.md
+++ /dev/null
@@ -1,32 +0,0 @@
-# We can't code good (so take LLM hype with a grain of salt)
-
-Software engineering is a young field compared to most other fields.
-
-All studies about software engineering projects I have seen say that most projects are late, over budget, and underdeliver.
-
-(A great article did some statistics to estimate the percentage of software engineers who have never seen worked on a successful project.
-The result was discouraging.
-I have never found this article again, please contact me if you know it.)
-
-Consider CRUD applications, which most software engineers consider to be the lowest kind of work.
-A massive amount of CRUD applications have been developed, and [I believe we are not effective at churning them out](crud_is_an_important_unsolved_problem.md).
-
-[What we know we don't know: empirical software engineering](https://www.hillelwayne.com/talks/ese/ddd/) is an interesting talk by Hillel Wayne that elaborates on this topic.
-
-The talk claims that researchers who try to prove that any engineering practice has a significant effect on delivery mostly fail.
-Apparently, code review seems to be one of the few practices that has been proven to be effective but its effectivity is small compared to sleeping well, not being stressed, or working the right amount of hours.
-
-This includes practices that most of us believe have significant effects, such as using dynamic or static typing, abbreviating or not identifiers, and many others.
-
-We all believe that some practices are effective, yet this does not seem to be measurable.
-
-I think experience, good team work, smarts, and motivation help.
-(And I believe there are certainly things that *hinder* software engineering!)
-We have been able to deliver good works of engineering, and some things seem to work, but we can't code good yet.
-
-So *any* claim of *any* practice being effective should be taken with a dose of healthy skepticism.
-I do not even know how to measure software engineering effectivity; I can use my experience to compare and try to infer conclusions, but I know my conclusions can be wrong.
-
-I am sure that at some point our discipline will be as mature as other disciplines, but think how much effort it took other disciplines to get there.
-But until then, we should be humble and not make bold claims, and we should question the incentives of those who make bold claims.
-Perhaps some of those bold claims turn out to be true, but the longer that you have been in this field, the more bold claims you will have seen fail.
diff --git a/programming/when_to_code.md b/programming/when_to_code.md
deleted file mode 100644
index c0bead9e..00000000
--- a/programming/when_to_code.md
+++ /dev/null
@@ -1,18 +0,0 @@
-# When to code
-
-Our job as programmers is to solve real-world problems in a satisfactory manner.
-As we are coders, our first instinct is to jump in and write some code.
-This is often a mistake, consider:
-
-* Coding is very expensive.
-It takes a lot of time, not just coding, but gathering requirements from the business people, deploying it to a production environment, doing end-user testing, maintaining it...
-* There is a massive amount of software out there- chances are, someone had the need before and paid someone to do it.
-If it is a common problem, there is a chance that there is something that will fit exactly your needs (and if it doesn't fit exactly, you might need to rethink your requirements- someone went over the full process and probably you have not- they might have figured out something you have not).
-Perhaps it's got a price tag on it, and maybe you think it's excessive, but if they are commercializing it and they have a few customers, consider that the cost of development will be divided among all customers- and you might not have this advantage.
-* Even if there is no specific off-the-shelf software which solves your needs, some generic software such as an spreadsheet or a content management system, or some specific features such as a word processor's mail merge functionality might come close enough to your needs.
-It is likely that at some point you will outgrow it (and software such as spreadsheets are specially amenable to being twisted and stretched terribly beyond their intended use case, resulting in terrible monsters which you should be wary of), but starting with them might help you frame the problem better than starting with code.
-
-Still, there are lots of uncharted territories- there are needs for which no decent off-the-shelf software exists, or you might have already examined all possibilities and they truly don't fit you.
-Or maybe you want to create a competing product, or doing something truly innovative.
-Or (this is a common scenario) you are not able to convince someone to use an off-the-shelf solution.
-In that case, you will have to code.
diff --git a/scripts/paperwm b/scripts/paperwm
new file mode 100755
index 00000000..c05285b8
--- /dev/null
+++ b/scripts/paperwm
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+set -uex
+
+test -f ~/.cache/paperwmpaperwm.github.com.v145.shell-extension.zip || {
+ wget https://extensions.gnome.org/extension-data/paperwmpaperwm.github.com.v145.shell-extension.zip -O ~/.cache/paperwmpaperwm.github.com.v145.shell-extension.zip
+}
+
+test -d ~/.local/share/gnome-shell/extensions/paperwm@paperwm.github.com || {
+ mkdir -p ~/.local/share/gnome-shell/extensions/paperwm@paperwm.github.com
+ cd ~/.local/share/gnome-shell/extensions/paperwm@paperwm.github.com
+ unzip ~/.cache/paperwmpaperwm.github.com.v145.shell-extension.zip
+}
+
+dconf load / <<EOF
+[org/gnome/shell]
+enabled-extensions=['paperwm@paperwm.github.com']
+EOF
diff --git a/wishlist.org b/wishlist.org
index 456158c7..8f9b4326 100644
--- a/wishlist.org
+++ b/wishlist.org
@@ -10,6 +10,7 @@ Todos se pueden comprar en Kindle, tapa blanda o el formato que salga más barat
- [[https://www.amazon.es/dp/0440539811/][The Illuminatus! Trilogy]]
- Libros de Ursula K. Le Guin o de Lois McMaster Bujold
- [[https://en.wikipedia.org/wiki/Salt_Fat_Acid_Heat_(book)][Salt Fat Acid Heat]]
+- [[https://www.amazon.es/Highlander-Original-Screenplay-Gregory-Widen/dp/1787747506/][Highlander (The Original Screenplay)]]
- [[https://en.wikipedia.org/wiki/Through_Darkest_Europe][Through Darkest Europe]]
** No ficción
@@ -25,7 +26,6 @@ Todos se pueden comprar en Kindle, tapa blanda o el formato que salga más barat
* Comida
- Agua de lúpulo
-- Bebida "Almdudler". [[https://www.amazon.es/Almdudler-27371-Original-500ml/dp/B01MS76JR0/]]
- Cosas raras para comer (pero sin sal)
- Ferrero Pocket Coffee descafeinado. [[https://www.ferrero.es/productos/helados/pocket-coffee-helados][La versión en helado]] no está mal, está disponible en algún bar.
- Vino/cocktails/vermú sin alcohol