rewrite jason post: inline references, build→grow, Jason→JSON
kill footer. weave 20+ cross-references into prose naturally. nested-lookup origin, webwords 42 languages, SLOP protocol, Salt states, Kubernetes, ago, remarkbox public domain. replace build with grow. swap most Jason to JSON, keep Jason for emotional beats only (falling in love, crawling back, every developer has a Jason, writing a custom encoder).
This commit is contained in:
parent
f141054977
commit
0d127bbe94
2 changed files with 28 additions and 119 deletions
|
|
@ -19,13 +19,13 @@ I fell in love with Jason on a Tuesday.
|
|||
|
||||
He arrived in my terminal at 2am, perfectly structured, every key quoted, every value in its place. Clean. Predictable. A kind of beautiful that makes you mass-delete your YAML configs & never look back. I whispered ``json.loads()`` & he opened up completely. No secrets. No ambiguity. No schema required. Just pure, naked data.
|
||||
|
||||
I got so high on Jason I rewrote three services that week. REST endpoints blooming like flowers. Every response a gift. Every request body a love letter wrapped in curly braces. I told my coworkers about him. I told strangers. I put ``application/json`` in my email headers as a joke that nobody laughed at. I didn't care. Jason understood me.
|
||||
I got so high on JSON I rewrote three services that week. REST endpoints blooming like flowers. Every response a gift. Every request body a love letter wrapped in curly braces. I told my coworkers about him. I told strangers. I put ``application/json`` in my email headers as a joke that nobody laughed at. I didn't care. Jason understood me.
|
||||
|
||||
|
|
||||
|
||||
Then came nested objects.
|
||||
|
||||
Seven levels deep. Keys named ``data`` containing keys named ``data`` containing keys named ``results`` containing a list of objects with keys named ``data``. I stared at my screen & felt nothing. Jason had become a maze. A fractal of repetition. I wrote ``response["data"]["data"]["results"][0]["data"]`` & something inside me broke.
|
||||
Seven levels deep. Keys named ``data`` containing keys named ``data`` containing keys named ``results`` containing a list of objects with keys named ``data``. I stared at my screen & felt nothing. JSON had become a maze. A fractal of repetition. I wrote ``response["data"]["data"]["results"][0]["data"]`` & something inside me broke.
|
||||
|
||||
I started seeing curly braces when I closed my eyes.
|
||||
|
||||
|
|
@ -34,19 +34,23 @@ I started seeing curly braces when I closed my eyes.
|
|||
:align: center
|
||||
:width: 400px
|
||||
|
||||
So I did what any rational person would do. I `wrote a library </dealing-with-pagination-in-python/>`_. I ripped ``nested_lookup()`` out of a paginated AWS ElastiCache nightmare & published it to `PyPI <https://pypi.org/project/nested-lookup/>`_. Public domain. Now nobody has to type ``response["data"]["data"]["results"][0]["data"]`` ever again. You just say ``nested_lookup("data", response)`` & it hands you every match from every depth. That frustration became a `real tool <https://git.unturf.com/python/nested-lookup>`_ that strangers install with ``pip``.
|
||||
|
||||
Love makes you grow things.
|
||||
|
||||
|
|
||||
|
||||
But then. *Then.* I discovered ``jq``.
|
||||
|
||||
Suddenly Jason looked beautiful again. Pipes & filters & recursion. I could reach inside him & pull out exactly what I needed. ``.data.data.results[].data`` & there it sat, clean & simple, streaming through my terminal like music. I loved him more than ever. I tattooed ``{}`` on my soul. Metaphorically. Mostly.
|
||||
Suddenly JSON looked beautiful again. Pipes & filters & recursion. I could reach inside him & pull out exactly what I needed. ``.data.data.results[].data`` & there it sat, clean & simple, streaming through my terminal like music. I loved him more than ever. I tattooed ``{}`` on my soul. Metaphorically. Mostly.
|
||||
|
||||
I built an entire platform on Jason. APIs talking to APIs talking to APIs. A cathedral of structured data. Every microservice a verse in a love poem nobody asked for. Everything hummed. I hummed. We hummed together.
|
||||
I grew an entire platform on JSON. APIs talking to APIs talking to APIs. I `output entire AWS VPCs to JSON </output-all-instance-identifiers-of-an-aws-vpc-to-json/>`_. I `piped Salt return data through json.loads() & json.dumps() </filter-salt-stack-return-data-output/>`_. I made `webwords </webwords-is-a-minimal-viable-web-app-with-docker-in-as-many-languages-as-possible/>`_ return JSON from HTTP endpoints in `42 different languages </webwords-reaches-42-languages-the-ultimate-programming-kata/>`_. Same response. Same format. `Code golf editions </webwords-code-golf-minimal-implementation/>`_ too. Everything hummed. I hummed. We hummed together.
|
||||
|
||||
|
|
||||
|
||||
Then Jason started lying to me.
|
||||
Then JSON started lying to me.
|
||||
|
||||
Not on purpose. He can't help it. He has no comments. No way to explain himself. No way to say "this field got deprecated" or "this number actually represents a string because a vendor went unhinged." He just sits there, syntactically valid, semantically bankrupt. I got a ``"status": "success"`` response from a service that had clearly failed. It contained an error message. Inside a success response. Jason delivered it with a straight face. No emotion. No contradiction detected.
|
||||
Not on purpose. He can't help it. He has no comments. No way to explain himself. No way to say "this field got deprecated" or "this number actually represents a string because a vendor went unhinged." He just sits there, syntactically valid, semantically bankrupt. I once had to configure `nginx to throw a 503 as JSON </nginx-throw-503-maintenance-json-for-all-requests/>`_ because APIs expect JSON bodies even when everything catches fire. JSON delivered ``"status": "success"`` from a service that had clearly failed. An error message inside a success response. Straight face. No emotion. No contradiction detected.
|
||||
|
||||
I mass-deleted my jq aliases & stared at a wall.
|
||||
|
||||
|
|
@ -60,7 +64,9 @@ Maybe I love that about him.
|
|||
|
||||
|
|
||||
|
||||
Highs kept coming. ``json.dumps(obj, indent=2)`` feels like meditation. Watching your data unfold, properly indented, every comma in place. Peace lives there. I wrote a script that pretty-prints every API response to stdout & I just watched it scroll. For hours. My partner asked if I felt okay. I said I felt better than okay. I responded ``{"status": "transcendent"}``.
|
||||
Highs kept coming. ``json.dumps(obj, indent=2)`` feels like meditation. Watching your data unfold, properly indented, every comma in place. Peace lives there. I `compared Node.js & Python performance </comparing-nodejs-and-python-performance-with-openai-client/>`_ pushing JSON through OpenAI endpoints. I `benchmarked Elixir & Phoenix </comparing-elixir-and-phoenix-performance-with-openai-client/>`_ doing it too. I grew `free LLM endpoints</free-llm-endpoints-dynamic-distributed-model-inference-client-uncloseai/>`_ & a whole `SLOP protocol </introducing-slop/>`_ because JSON API standards should stay simple. ``Content-Type: application/json``. No fuss. No proprietary lock-in.
|
||||
|
||||
My partner asked if I felt okay. I responded ``{"status": "transcendent"}``.
|
||||
|
||||
They did not find this charming.
|
||||
|
||||
|
|
@ -68,7 +74,7 @@ They did not find this charming.
|
|||
|
||||
Lows kept coming too.
|
||||
|
||||
Trailing commas. Jason rejects trailing commas. You know who accepts trailing commas? Python. JavaScript. Every language that has ever loved its users. But not Jason. One misplaced comma after a final element & he shuts down completely. No partial parse. No helpful error. Just ``Expecting value: line 47 column 1``. Line 47. A comma sat on line 46. Jason can't even point at a wound accurately.
|
||||
Trailing commas. JSON rejects trailing commas. You know who accepts trailing commas? Python. JavaScript. Every language that has ever loved its users. But not JSON. One misplaced comma after a final element & he shuts down completely. No partial parse. No helpful error. Just ``Expecting value: line 47 column 1``. Line 47. A comma sat on line 46. JSON can't even point at a wound accurately.
|
||||
|
||||
I mass-deleted a config file out of spite & rebuilt it from memory.
|
||||
|
||||
|
|
@ -76,7 +82,7 @@ It had a trailing comma.
|
|||
|
||||
|
|
||||
|
||||
I tried TOML once, during a low point. TOML plays a rebound you date to make Jason jealous. Nice enough. Comments. Native datetime support. But TOML doesn't scale. TOML fits like a studio apartment. Jason stretches like a warehouse you partition however you want. Sure, a warehouse without labels on anything, where you lose your keys constantly, but it stays *yours*.
|
||||
I tried TOML once, during a low point. TOML plays a rebound you date to make JSON jealous. Nice enough. Comments. Native datetime support. But TOML doesn't scale. TOML fits like a studio apartment. JSON stretches like a warehouse you partition however you want. Sure, a warehouse without labels on anything, where you lose your keys constantly, but it stays *yours*.
|
||||
|
||||
I went back to Jason by Friday.
|
||||
|
||||
|
|
@ -87,26 +93,26 @@ I went back to Jason by Friday.
|
|||
|
||||
|
|
||||
|
||||
Here sits truth about loving Jason. He lives everywhere. He speaks as lingua franca of a connected world. He rides inside every webhook, every REST API, every config file that gave up on cleverness. He sits in your browser's local storage. He hides in your package.json. He lurks in your Jupyter notebooks. He underpins modern computing & still can't support integers larger than 2^53 without losing precision.
|
||||
Here sits truth about loving JSON. He lives everywhere. He speaks as lingua franca of a connected world. He rides inside every webhook, every REST API, every `config file that gave up on cleverness </configuration-management-vs-remote-execution/>`_. He sits in your browser's local storage. He hides in your package.json. He lurks in your Jupyter notebooks. He underpins modern computing & still can't support integers larger than 2^53 without losing precision.
|
||||
|
||||
I know this. I know all of his flaws. Missing comments. Trailing comma fascism. Numbers that silently overflow. Strings that can't contain a literal newline without escaping. A complete absence of a date type, forcing every API to invent its own ISO 8601 interpretation. No binary data support. ``null`` existing as a value where you can never tell if it means "absent" or "intentionally empty" or "a developer forgot."
|
||||
I know this. I know all of his flaws. Missing comments. Trailing comma fascism. Numbers that silently overflow. `Strings that can't serialize nested objects without warnings </turn-python-dict-into-a-keyvalue-string/>`_. A complete absence of a date type, forcing every API to invent its own ISO 8601 interpretation. No binary data support. ``null`` existing as a value where you can never tell if it means "absent" or "intentionally empty" or "a developer forgot."
|
||||
|
||||
I know all of this & I love him anyway.
|
||||
|
||||
.. image:: /uploads/2026/02/jason-cycle.svg
|
||||
:alt: bipolar love cycle diagram showing discover, euphoria, build, frustration, betrayal, try alternatives, crawl back
|
||||
:alt: bipolar love cycle diagram showing discover, euphoria, grow, frustration, betrayal, try alternatives, crawl back
|
||||
:align: center
|
||||
:width: 500px
|
||||
|
||||
|
|
||||
|
||||
Some nights I write YAML & think of him. YAML with its invisible whitespace traps & its "Norway problem" where ``NO`` becomes ``false``. Do you even know `who owns YAML <https://russell.ballestrini.net/who-owns-yaml/>`_? YAML who looks friendly but will silently interpret ``3.10`` as a float ``3.1`` & destroy your Python version matrix. YAML who needs you to memorize which scalars evaluate truthy. At least Jason stays honest about difficulty. At least Jason fails loudly. At least Jason has never turned a country code into a boolean.
|
||||
Some nights I write YAML & think of him. YAML with its invisible whitespace traps & its "Norway problem" where ``NO`` becomes ``false``. Do you even know `who owns YAML </who-owns-yaml/>`_? I spent a decade writing `Salt states </create-your-own-fleet-of-servers-with-digital-ocean-and-salt-cloud/>`_ in YAML. I `managed users </understanding-salt-stack-user-and-group-management/>`_, `replaced Nagios </replace-the-nagios-scheduler-and-nrpe-with-salt-stack/>`_, `deployed Kubernetes manifests </selenium-grid-on-kubernetes/>`_. All YAML. YAML who looks friendly but will silently interpret ``3.10`` as a float ``3.1`` & destroy your Python version matrix. YAML who needs you to memorize which scalars evaluate truthy. At least JSON stays honest about difficulty. At least JSON fails loudly. At least JSON has never turned a country code into a boolean.
|
||||
|
||||
|
|
||||
|
||||
I think every developer has a Jason. A technology you can't quit. One that drives you to mass-delete your dotfiles at 3am & then reinstall everything by sunrise because every alternative feels worse. One that makes you mass-write blog posts about your feelings because ``json.dumps(feelings)`` returns ``TypeError: Object of type 'heartbreak' is not JSON serializable``.
|
||||
I think every developer has a Jason. A technology you can't quit. `Programming feels like alchemy </programming-is-like-alchemy/>`_ & JSON sits at a center of every transmutation. One that drives you to mass-delete your dotfiles at 3am & then reinstall everything by sunrise because every alternative feels worse. One that makes you mass-write blog posts about your feelings because ``json.dumps(feelings)`` returns ``TypeError: Object of type 'heartbreak' is not JSON serializable``.
|
||||
|
||||
You'd have to write a custom encoder for that. & you know what? I would. For Jason, I would write a custom encoder.
|
||||
You'd have to write a custom encoder for that. & you know what? I would. For Jason, I would write a custom encoder. I wrote `ago <https://pypi.org/project/ago/>`_ to `humanize time deltas </miniuri-parser-and-ago-human-timedelta/>`_. I wrote `nested-lookup <https://pypi.org/project/nested-lookup/>`_ to survive his nesting. I `open sourced Remarkbox & MakePostSell into public domain </russell-open-sources-remarkbox-and-make-post-sell-into-public-domain/>`_ because code should outlast its author. For Jason, a ``LoveEncoder`` feels like a small ask.
|
||||
|
||||
``json.dumps(feelings, cls=LoveEncoder, indent=2, ensure_ascii=False)``
|
||||
|
||||
|
|
@ -124,100 +130,3 @@ For a while.
|
|||
"trailing_comma": false,
|
||||
"love": true
|
||||
}
|
||||
|
||||
----
|
||||
|
||||
Further Reading
|
||||
===============
|
||||
|
||||
Everything referenced above comes from years of building with JSON, fighting with YAML, & writing Python libraries to survive both. Here sits a map of related work across this site.
|
||||
|
||||
**Solving Nested JSON**
|
||||
|
||||
I wrote `nested-lookup <https://pypi.org/project/nested-lookup/>`_ so nobody has to type ``response["data"]["data"]["results"][0]["data"]`` ever again. A Python library. Public domain. Born from exactly that frustration.
|
||||
|
||||
- `nested-lookup on PyPI <https://pypi.org/project/nested-lookup/>`_
|
||||
- `nested-lookup source code <https://git.unturf.com/python/nested-lookup>`_
|
||||
- `Dealing with pagination in Python </dealing-with-pagination-in-python/>`_ — where ``nested_lookup()`` first appeared, extracted from paginated AWS ElastiCache API work. Origin story of a library.
|
||||
|
||||
**JSON in Production**
|
||||
|
||||
Every API I ever built speaks Jason fluently.
|
||||
|
||||
- `Output all instance identifiers of an AWS VPC to JSON </output-all-instance-identifiers-of-an-aws-vpc-to-json/>`_
|
||||
- `Nginx throw HTTP 503 maintenance JSON for all requests </nginx-throw-503-maintenance-json-for-all-requests/>`_
|
||||
- `Filter Salt Stack return data output </filter-salt-stack-return-data-output/>`_ — pipes ``--out=json`` through ``json.loads()`` & ``json.dumps()``
|
||||
- `Introducing SLOP </introducing-slop/>`_ — Simple Language Open Protocol. RESTful JSON API standard for ML services. No fuss. No proprietary lock-in.
|
||||
|
||||
**JSON HTTP Servers in 42+ Languages**
|
||||
|
||||
webwords proves JSON speaks every language. Same endpoint. Same response. 42 implementations.
|
||||
|
||||
- `webwords: a minimal viable web app with Docker in as many languages as possible </webwords-is-a-minimal-viable-web-app-with-docker-in-as-many-languages-as-possible/>`_
|
||||
- `webwords reaches 42 languages </webwords-reaches-42-languages-the-ultimate-programming-kata/>`_
|
||||
- `webwords code golf: minimal HTTP servers </webwords-code-golf-minimal-implementation/>`_
|
||||
|
||||
**API Performance & JSON Throughput**
|
||||
|
||||
How fast can you push JSON through a wire? Depends on who carries it.
|
||||
|
||||
- `Comparing Node.js & Python performance with OpenAI Client </comparing-nodejs-and-python-performance-with-openai-client/>`_
|
||||
- `Comparing Elixir & Phoenix performance with OpenAI Client </comparing-elixir-and-phoenix-performance-with-openai-client/>`_
|
||||
- `Free LLM endpoints & dynamic distributed model inference </free-llm-endpoints-dynamic-distributed-model-inference-client-uncloseai/>`_
|
||||
- `Free Hermes ML & uncloseai.js </free-hermes-ai-unturf-com-uncloseai/>`_
|
||||
|
||||
**YAML: A Complicated Relationship**
|
||||
|
||||
Every mention of YAML in this story comes from lived experience. I wrote Salt states for a decade. YAML ran my infrastructure. I still have opinions.
|
||||
|
||||
- `who owns YAML? </who-owns-yaml/>`_ — who controls parsers across 42 languages & what license covers them
|
||||
- `Configuration management vs remote execution </configuration-management-vs-remote-execution/>`_
|
||||
- `Configuration management & a golden image </configuration-management-and-the-golden-image/>`_
|
||||
- `Add a custom header to Salt managed files using pillar & Jinja </add-a-custom-header-to-all-salt-managed-files-using-pillar-and-jinja-templates/>`_
|
||||
- `Create your own fleet of servers with Digital Ocean & salt-cloud </create-your-own-fleet-of-servers-with-digital-ocean-and-salt-cloud/>`_
|
||||
- `Replace Nagios with Salt Stack </replace-the-nagios-scheduler-and-nrpe-with-salt-stack/>`_ — Salt remote execution with ``--out=json``
|
||||
|
||||
**Kubernetes & Docker (YAML All Over Again)**
|
||||
|
||||
YAML manifests define everything. JSON rides inside every container's API response.
|
||||
|
||||
- `Minikube </minikube/>`_
|
||||
- `Selenium grid on Kubernetes </selenium-grid-on-kubernetes/>`_
|
||||
- `Running DynamoDB Local on CircleCI 2.0 </running-dynamodb-local-service-container-on-circleci-2/>`_
|
||||
|
||||
**Python Libraries I Published to PyPI**
|
||||
|
||||
nested-lookup solved JSON. ago solved time. Public domain. Code outlasts authors.
|
||||
|
||||
- `nested-lookup <https://pypi.org/project/nested-lookup/>`_ — deeply nested document lookup
|
||||
- `ago <https://pypi.org/project/ago/>`_ — human readable time deltas
|
||||
- `miniuri parser & ago human timedelta </miniuri-parser-and-ago-human-timedelta/>`_ — where ago got announced
|
||||
- `ago.py 0.0.4 release </ago-py-human-readable-timedelta0-0-4-release/>`_
|
||||
- `Build release pipelines on S3 with s3p </build-release-pipelines-on-s3-with-s3p/>`_
|
||||
|
||||
**Python Data Wrangling**
|
||||
|
||||
Every ``json.loads()`` & ``json.dumps()`` call connects to deeper Python knowledge.
|
||||
|
||||
- `Turn a Python dict into a key=value string & back again </turn-python-dict-into-a-keyvalue-string/>`_ — warns about nested objects
|
||||
- `Filtering AWS resources with Boto3 </filtering-aws-resources-with-boto3/>`_ — navigating JSON API structures
|
||||
- `Setting region programmatically in Boto3 </setting-region-programmatically-in-boto3/>`_
|
||||
- `AWS nvme to block mapping </aws-nvme-to-block-mapping/>`_ — queries EC2 metadata JSON endpoint
|
||||
- `List all installed package names in Python </list-all-installed-package-names-in-python/>`_
|
||||
|
||||
**Open Source & Public Domain**
|
||||
|
||||
Jason lives in public domain libraries because code should outlast its author.
|
||||
|
||||
- `Russell open sources Remarkbox & MakePostSell into public domain </russell-open-sources-remarkbox-and-make-post-sell-into-public-domain/>`_
|
||||
- `A homegrown Python bread crumb module </a-homegrown-python-bread-crumb-module/>`_ — first public domain release
|
||||
- `Tips for getting pull requests approved </tips-for-getting-pull-requests-approved/>`_
|
||||
- `Growing a 454-page ML reference manual in 5 days </growing-a-book-in-5-days-ml-and-devops/>`_ — ML inference across 58 languages
|
||||
|
||||
**Philosophy**
|
||||
|
||||
Every love story has a worldview underneath it.
|
||||
|
||||
- `Programming feels like alchemy </programming-is-like-alchemy/>`_
|
||||
- `Risk, process, & balance </risk-process-and-balance/>`_
|
||||
- `Simplify deployments with Upstart & uWSGI </simplify-deployments-with-upstart-and-uwsgi/>`_
|
||||
|
|
|
|||
|
|
@ -29,16 +29,16 @@
|
|||
<polygon fill="#888888" stroke="#888888" points="110.22,-512.2 106.72,-502.2 103.22,-512.2 110.22,-512.2"/>
|
||||
<text text-anchor="middle" x="138.72" y="-522.82" font-family="monospace" font-size="9.00" fill="#888888">json.loads()</text>
|
||||
</g>
|
||||
<!-- build -->
|
||||
<!-- grow -->
|
||||
<g id="node3" class="node">
|
||||
<title>build</title>
|
||||
<title>grow</title>
|
||||
<ellipse fill="#50fa7b" fill-opacity="0.200000" stroke="#666666" cx="61.72" cy="-397.39" rx="59.29" ry="22.76"/>
|
||||
<text text-anchor="middle" x="61.72" y="-400.59" font-family="monospace" font-size="11.00" fill="#50fa7b">build</text>
|
||||
<text text-anchor="middle" x="61.72" y="-400.59" font-family="monospace" font-size="11.00" fill="#50fa7b">grow</text>
|
||||
<text text-anchor="middle" x="61.72" y="-388.59" font-family="monospace" font-size="11.00" fill="#50fa7b">everything</text>
|
||||
</g>
|
||||
<!-- euphoria->build -->
|
||||
<!-- euphoria->grow -->
|
||||
<g id="edge2" class="edge">
|
||||
<title>euphoria->build</title>
|
||||
<title>euphoria->grow</title>
|
||||
<path fill="none" stroke="#888888" d="M97.83,-466.3C92.14,-455.6 84.59,-441.41 77.87,-428.76"/>
|
||||
<polygon fill="#888888" stroke="#888888" points="80.94,-427.09 73.16,-419.9 74.76,-430.37 80.94,-427.09"/>
|
||||
<text text-anchor="middle" x="109.22" y="-440.82" font-family="monospace" font-size="9.00" fill="#888888">indent=2</text>
|
||||
|
|
@ -49,9 +49,9 @@
|
|||
<ellipse fill="#ff5555" fill-opacity="0.200000" stroke="#666666" cx="54.72" cy="-310.76" rx="54.94" ry="18"/>
|
||||
<text text-anchor="middle" x="54.72" y="-307.96" font-family="monospace" font-size="11.00" fill="#ff5555">frustration</text>
|
||||
</g>
|
||||
<!-- build->frustration -->
|
||||
<!-- grow->frustration -->
|
||||
<g id="edge3" class="edge">
|
||||
<title>build->frustration</title>
|
||||
<title>grow->frustration</title>
|
||||
<path fill="none" stroke="#888888" d="M59.92,-374.6C59.02,-363.72 57.92,-350.47 56.97,-338.93"/>
|
||||
<polygon fill="#888888" stroke="#888888" points="60.46,-338.63 56.14,-328.95 53.48,-339.2 60.46,-338.63"/>
|
||||
<text text-anchor="middle" x="93.22" y="-349.56" font-family="monospace" font-size="9.00" fill="#888888">7 levels deep</text>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 6.6 KiB |
Loading…
Add table
Add a link
Reference in a new issue