Sr. Content Developer at Microsoft, working remotely in PA, TechBash conference organizer, former Microsoft MVP, Husband, Dad and Geek.
156971 stories
·
33 followers

Worse is better: JSON versus XML

1 Share

JSON is not a good data-interchange format.

This article is part of a series called Worse is better, in which I muse on technologies and techniques that became popular despite superior alternatives. Think VHS versus Betamax.

In this article, I will argue that XML is superior to JSON in most respects.

Lightweight XML #

Depending on how old you are, I guess that you have one of two reactions. If you started programming around 2015 you may simply ask: "What's XML?" If your programming career reaches further back, your reaction may be one of incredulity: "Oh my God, how can you say that?! Good riddance that SOAP, WS-(death)*, and XSLT are things of the past."

Indeed, and I don't miss them, either.

While that reaction is typical, it confuses cause and effect. SOAP and similar standards weren't cumbersome and overly complex because of XML. They managed to be awkward and enterprisey all by themselves. As a thought experiment, you could define all the payloads and specifications of SOAP as JSON, but it would, ironically, be even more verbose, because you'd have to invent a schema language and so on.

XML doesn't have to be heavy or formal. You may find the informality of JSON an advantage. Just write a document:

{
  "author""Peter Watts",
  "title""Blindsight"
}

While this is indeed easy and requires no ceremony, what prevents you from doing the same in XML?

<book>
  <author>Peter Watts</author>
  <title>Blindsight</title>
</book>

You don't have to first define a schema. You don't have to declare a namespace. You don't have to add an XML declaration.

But you can, if you need to. XML allows gradual enhancement. If, sometime later, you find that a formal, machine-readable document specification would be useful, you can use XSD. And yes, I'm aware of JSON Schema; I hope the reader can see the irony that such a thing exists.

Sweet spots #

Like any other technology, XML is not a one-size-fits-all technology. I think the ideal scenario for XML is interoperability. While I'm aware that modern systems handle JSON as well as XML, I would still prefer XML for most data exchange tasks. The main driver for that decision would be the possibility to define document schemas. As Alexis King argues in a slightly different context, the lack of static types or, here, a machine-readable schema, does not entail the absence of a specification. Only, as suggested by Hyrum's law, the contract is implicit.

XML comes with a standard schema language, a standardized way to version documents, a standard query language, a standard for streaming parsers, etcetera. Of course, nothing prevents you from inventing similar technologies for JSON, and I'm sure someone already has. Even so, XML is a more mature format. Why reinvent the wheel?

A less ideal use of XML is for configuration files. I know I've lost that fight, but JSON is not a good format for configuration files. The most obvious problem with JSON is the lack of support for comments. And I know that various tools and editors allow comments in various proprietary formats, but it's not part of the standard. XML, on the other hand, has a standard for comments.

You may argue that XML is less readable than JSON, and I will partially agree, even though with good tools such as syntax colouring I find the difference marginal. The same goes with editor experiences. Most code editors will help you with XML to the same degree that they will help you with JSON. And again, if you work with a document that has a defined schema, the editor can help you more by suggesting and auto-filling elements.

But really, neither XML nor JSON are perfect configuration file formats. I wonder if such a thing even exists.

Where JSON shines #

Where would I choose JSON? To be clear, I would pragmatically choose JSON in lots of cases today, simply because that's the expected format, and having to defend a less popular choice is rarely worth it.

Ironically, the kind of architecture we today call SPAs got started as AJAX, where the X stood for, you guessed it, XML. Even so, the J stands for JavaScript, so it makes more sense to use JavaScript Object Notation (JSON). That's what modern SPAs do, and I would too, particularly if the service in question was a BFF.

Size #

I'd be surprised if you've made it so far and haven't though of size as a factor in favour of JSON. It's true that XML is more verbose than JSON. Depending on the actual schema and payload, the size difference could conceivably be more than a factor of two; compare <die><roll>4</roll></die> (25 characters) to {"roll":4} (10 characters). On the other hand, for other kinds of payloads, the difference might only be a small percentage.

In my experience the size difference doesn't matter that much. Often, other factors also play a role: Network latency for transmission, or block size for storage. And when performance really is a consideration, JSON may be too big, too.

Conclusion #

Although XML is generally and unfairly loathed as an old-fashioned legacy or enterprise technology, in most aspects it's a format superior to the more popular JSON.

XML has a rich ecosystem of mature standards, including a schema language that even supports sum types. While you could reimplement many of these in JSON (which has already been done), why reinvent the wheel?

Next: Worse is better: C# versus F#.


This blog is totally free, but if you like it, please consider supporting it.
Read the whole story
alvinashcraft
just a second ago
reply
Pennsylvania, USA
Share this story
Delete

Combining Google Stitch with the GitHub Copilot Coding Agent

1 Share

UI generation and background coding agents are two of the "AI" tools that really changed my way of working. Together, they close a gap that's been annoying me for a while: the coding agent still needs someone to describe what the UI should look like, and that someone is usually me, typing a wall of text into an issue and hoping for the best.

Google Stitch generates UI screens (HTML/CSS, Tailwind, Flutter, SwiftUI, whatever…) from a prompt or a sketch. The GitHub Copilot coding agent picks up an issue and produces a pull request in the background, without you sitting in the editor. In this post we look at how to connect the two through MCP, so the coding agent stops guessing at layout, spacing and colors, and starts reading an actual design spec.

Here's how to wire it up, and where it still needs a human in the loop.

My first approach: screenshots in the issue body

My first attempt was to design something in Stitch, paste a screenshot into a GitHub issue, and assign it to Copilot. It kind of works. The coding agent can look at the image, but it's reconstructing layout, colors and spacing from pixels, not from data. You'll get something that looks close, then spend your review cycle nudging padding and hex codes back into alignment. That's not the coding agent's fault — it's just working with less information than it needs.

The right approach: give the agent the design data, not a picture of it

Stitch doesn't just produce a picture. Alongside the generated screens it exposes the underlying HTML, design tokens and a design.md file describing the whole design system — colors, typography, spacing, component patterns. If the coding agent can pull that structured data itself, it stops guessing.

That's what the Stitch MCP server is for. Point the coding agent at it, and instead of an issue that says "make it look like the attached screenshot," you get an issue that says "build the dashboard screen from the Stitch project, screen ID X" — and the agent fetches the real spec.

Step 1: Design the screens in Stitch

Nothing unusual here — describe the flow in Stitch the way you normally would.


Stitch can generate several connected screens in one pass and keeps them visually consistent. Note the project ID and screen IDs — you'll reference them later.

Step 2: Register the Stitch MCP server on the repository

The coding agent runs headless in the cloud, and it doesn't support remote MCP servers that use OAuth. So skip the gcloud auth login dance for this scenario and use Stitch's official remote MCP server with an API key instead — it's a plain HTTP header, not an OAuth flow, and the cloud agent handles that fine.

Generate a key: in Stitch, open your profile menu → Stitch SettingsAPI KeysCreate Key.


Store it as a repository secret prefixed with COPILOT_MCP_ (this prefix is required for the coding agent to pick it up): go to Settings → Secrets and variables → Actions, add COPILOT_MCP_STITCH_API_KEY.

Then in the repository: Settings → Copilot → MCP servers, and add:

json

{
  "mcpServers": {
    "stitch": {
      "type": "http",
      "url": "https://stitch.googleapis.com/mcp",
      "headers": {
        "X-Goog-Api-Key": "$COPILOT_MCP_STITCH_API_KEY"
      },
      "tools": ["*"]
    }
  }
}

Save it. This same MCP configuration is also shared with Copilot code review, so your reviewer agent gets access to the same design context.

A couple of things worth knowing before you rely on this:

  • The coding agent only supports MCP tools, not resources or prompts, so anything the Stitch server exposes as a resource won't show up.
  • By default the agent doesn't get write-access MCP tools. Stitch's tools here are all reads (fetch screens, fetch code, fetch design tokens), so that's not a problem — but double-check the tool list if you add other design-related MCP servers later.

Step 3: Write the issue against the design, not around it

With the MCP server registered, an issue can now point straight at the Stitch project instead of re-explaining the design in prose:

Implement the dashboard screen from Stitch project 7-minutes fitness trainer, screen dashboard. Use the Stitch MCP tools to fetch the HTML and design tokens, then build it as a React + Tailwind component under src/components/Dashboard. Match spacing and color tokens exactly. Existing repo conventions apply — see CONTRIBUTING.md.

Assign it to Copilot. The agent fetches the screen HTML and design tokens through the MCP tools, translates that into your actual stack, and opens a draft pull request. Because the GitHub MCP server is also available by default, it can cross-reference the issue and push commits as it goes, same as any other coding agent task.

Step 4: Review like you would any other PR

The generated code won't be pixel-perfect — code generation from a design spec still isn't deterministic, and you'll see small deviations in spacing or font-weight here and there. Treat the Stitch screen as the source of truth and the PR as a first draft, not a final one. If your repo has the Playwright MCP server enabled (it's on by default alongside GitHub's), you can ask the agent to take a screenshot of the rendered component and compare it against the Stitch export as part of the same task — closes the loop without you doing it manually.

Wrapping up

The interesting part isn't that AI can generate a screen or that AI can write a pull request — both have been true for a while. It's that MCP lets the coding agent read the actual design data instead of a description of it, which is the difference between "close enough" and "matches the spec."

More information

Read the whole story
alvinashcraft
just a second ago
reply
Pennsylvania, USA
Share this story
Delete

Track model usage with the OpenShift AI 3.4 usage dashboard

1 Share

When organizations scale their AI initiatives, the most challenging question isn't "Does the model work?" but "How do we track model usage for accurate chargeback, and do we have the capacity for tomorrow's workload?" Lack of visibility into consumption is a critical blocker for effective cost attribution and capacity planning.

Red Hat OpenShift AI 3.4 directly addresses the immediate need for visibility into model consumption. It introduces a new usage dashboard (Figure 1) that appears in the OpenShift AI console when you deploy Models-as-a-Service (MaaS), and provides administrators with usage tracking and oversight of API calls and token consumption for their managed AI models.

OpenShift AI usage dashboard displaying overview metrics for total tokens, requests, errors, and success rate.
Figure 1: The new usage dashboard in the Red Hat OpenShift AI console.

The usage dashboard provides the data necessary to monitor model consumption and manage resources:

  • Granular usage data: View detailed consumption metrics including token consumption, total API requests, and rate-limited calls.
  • Drill-down views: Filter usage data by subscription, user, and model within a sortable and filterable table.
  • Time-based filtering: Analyze usage trends over specific time frames using filters for one hour, 24 hours, three days, seven days, 14 days, or one month.
  • Real-time status: The dashboard shows high-level "box metrics," displaying Total Tokens, Total Requests, Total Errors, Success Rate, and Active Users.
  • Export for finance: The export function for usage data provides a basis for preliminary chargeback and cost attribution. (Note: Administrators should review the limitations mentioned later in this article regarding long-term retention and accuracy for official billing purposes.)

Tracking MaaS usage in the technology preview

The usage dashboard is introduced as a technology preview in Red Hat OpenShift AI 3.4. While it provides valuable information for showback, it also has limitations that we are actively working on. The main one is the cardinality risk that stems directly from the current architecture, which relies on Prometheus metrics.

Figure 2 depicts the successful flow of an inference call through MaaS. The process starts when a request from a client or an agent reaches the Istio Gateway.

Flowchart mapping numbered request steps between a client, Istio Gateway, Kuadrant Wasm Shim, MaaS API, and an inference server.
Figure 2: High-level flow of an inference call.

The Gateway passes the request to Kuadrant, the open source project providing API gateway technology within Red Hat Connectivity Link, where the flow proceeds through two components:

  1. Authorization: Authorino authenticates the user and authorizes the request via the MaaS API, which validates its API key.
  2. Rate limiting: Once authorized, the request proceeds to the rate limiter Limitador, which checks the request count against the predefined subscription limit. If the limit is exceeded, an HTTP 429 Too Many Requests error is returned immediately to the client.

If the request is authorized and within the defined limits, it is routed back through the Istio Gateway to the inference server (running the LLM). The final response from the inference server returns through the Istio Gateway and Kuadrant back to the client.

Limitador exposes three counters that track usage metrics during this process:

  • authorized_calls tracks successful API calls.
  • rate_limited tracks API calls that exceed the defined limit.
  • authorized_hits quantifies the consumed tokens.

To achieve user-level tracking, we apply labels to the metrics stream of Limitador. The following example shows the TelemetryPolicy used for injecting these labels:

apiVersion: extensions.kuadrant.io/v1alpha1
kind: TelemetryPolicy
metadata:
  name: maas-telemetry
  namespace: openshift-ingress
  labels:
    app.kubernetes.io/part-of: maas-observability
spec:
  metrics:
    default:
      labels:
        model: responseBodyJSON("/model")
        user: auth.identity.userid
        # Subscription metadata for usage attribution and billing
        subscription: auth.identity.selected_subscription
        organization_id: auth.identity.subscription_info.organizationId
        cost_center: auth.identity.subscription_info.costCenter
  targetRef:
    group: gateway.networking.k8s.io
    kind: Gateway
    name: maas-default-gateway

This is a relatively straightforward way to get the usage information. However, a large number of unique combinations of the labels (high cardinality) generates a massive amount of time series data stored in Prometheus that can potentially strain Prometheus resources and negatively affect its overall performance. Thus, these user-specific labels are only applied when an option to capture user information, which is disabled by default, is set in Models-as-a-Service. Before enabling this option, OpenShift AI administrators need to consider whether Prometheus can cope with the number of users, subscriptions, and models in their environment.

In addition to the potential high cardinality risk, the underlying use of Prometheus metrics introduces several other implications that can limit its suitability for precise chargeback and billing:

  • Data accuracy: Usage numbers can be inconsistent. Since Prometheus uses extrapolation and aggregation, the reported consumption can in some extreme cases be lower (missing initial calls in a time range) or higher (overcharging due to extrapolation) than the real usage.
  • Data retention: Prometheus is not intended for keeping data for a long time. Its default retention time of 15 days is insufficient for a full monthly billing cycle, which means that either the data would need to be queried more often or the retention time would need to be longer, requiring more storage space.
  • Data granularity: We might lack call-level granularity of the usage data due to the fact that aggregated values are being polled at a certain frequency. For example, when two authorized API calls are initiated between two polling cycles, we would only get their aggregated token consumption. Temporary polling interruptions can further reduce data granularity.

What's next? Access logs!

We started working on a major architectural improvement for the usage dashboard, planned for an upcoming Red Hat OpenShift AI release, that would address these limitations of the current solution by transitioning from Prometheus metrics to a structured access log solution.

In the revised architecture, a structured log will be generated at the gateway level for each inference call, instead of relying on metrics generated by Limitador. This log, based on the response from Limitador or the inference server, will capture the call's outcome, whether it was rate limited or authorized, and record the number of consumed tokens for authorized calls, among additional parameters.

While the appearance of the usage dashboard won't necessarily change, gathering the data using logs would help us to resolve the challenges we face with using Prometheus metrics:

  • The logs are designed for efficient, long-term storage, successfully bypassing the high cardinality problem at scale and supporting multiple monthly billing cycles.
  • The logs provide per-call, event-like information. Because this data is not aggregated, it can be accurately and simply summarized over any time range, directly resulting in superior accuracy in the dashboard and superior granularity.

This approach could potentially simplify the export of usage data to external metering and cost-management systems.

Give it a try

The new usage dashboard, despite its current technology preview status and mentioned limitations, already provides useful data for critical tasks like cost attribution and capacity planning. As we are actively working on improving this feature and addressing the current architectural challenges, we encourage you to explore its features and share your feedback with us.

To get started with the dashboard, Red Hat OpenShift AI administrators must complete the following steps:

  1. Install the Cluster Observability Operator (COO).
  2. Enable MaaS observability, ensuring the spec.telemetry.captureUser setting is included. Remember to consider the cardinality risk to Prometheus resources before enabling this setting.
  3. Ensure the spec.dashboardConfig.observabilityDashboard option is set in the custom resource OdhDashboardConfig.

Completing these steps will add the usage dashboard to the OpenShift AI console (under Observe & monitorDashboard), and calls to your AI models will start appearing there.

To learn more about Models-as-a-Service, read the blog post Scaling enterprise AI: Delivering Models-as-a-Service with Red Hat OpenShift AI 3.4.

To start a 60-day trial of OpenShift AI for your current OpenShift cluster, request it here.

The post Track model usage with the OpenShift AI 3.4 usage dashboard appeared first on Red Hat Developer.

Read the whole story
alvinashcraft
25 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Introducing Smart Retry: Safer retries for transient Jenkins failures.

1 Share

Smart Retry

In many Jenkins environments, a failed build does not always mean the code is broken.

Sometimes a Kubernetes agent gets evicted. Sometimes a Git fetch is interrupted. Sometimes an artifact repository has a short outage. In all of these cases, the next manual rebuild often succeeds.

smartRetry is designed for exactly this kind of CI problem: transient failures that are worth retrying, without turning every failed step into an automatic rerun.

How it works

Read the whole story
alvinashcraft
51 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

LeRobot v0.6.0: Imagine, Evaluate, Improve

1 Share
Read the whole story
alvinashcraft
2 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

sqlite-utils 4.0rc3

1 Share

Release: sqlite-utils 4.0rc3

I hoped to release sqlite-utils 4.0 stable this weekend, but as I worked through the backlog of issues and PRs with a combination of Claude Fable 5 and GPT-5.5 the changeling since rc2 kept getting bigger.

The biggest new feature is support for introspecting and creating compound foreign keys - a feature that involves a subtle breaking change to table.foreign_keys and hence needed to land for the 4.0 stable release.

sqlite-utils also now follows SQLite's convention for case insensitive column names, which turned out to touch a bunch of different places at once.

Tags: projects, sqlite-utils, annotated-release-notes, gpt, claude-mythos-fable

Read the whole story
alvinashcraft
2 minutes ago
reply
Pennsylvania, USA
Share this story
Delete
Next Page of Stories