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

Are Async Database Inserts Really Faster in .NET?

1 Share
When inserting records into a database, should you use synchronous or asynchronous execution? In this article, I compare both approaches in .NET, explain when each one makes sense, and share performance insights based on benchmarking. If you assume async is always faster, you may want to take a closer look.
Read the whole story
alvinashcraft
just a second ago
reply
Pennsylvania, USA
Share this story
Delete

How To Use Colour Psychology In Your Writing

1 Share

What is colour psychology? How does colour affect what you write? In this post, we explore colour psychology for writers.

In movies, heroes wear white, and villains wear black. Everybody knows that. But why? Why are traffic signs often red, and hospital rooms green? It’s all in the colours. This article will help you understand the underlying psychology, and how you can apply it to your writing.

How To Use Colour Psychology In Your Writing

Writing is not possible without choosing colours. It’s the first choice that writers make, white paper and black ink. The stark contrast helps both reader and writer to concentrate on the story.

That story, however, is also full of colours! Writers can merely name them (easier for the reader). Or they can use descriptions to make the readers infer them (engages the reader more).

The choice of these colours is not coincidental. Or at least, it shouldn’t be. Colours work on more levels than just the text. This article will introduce you to colour psychology and how to use it in your writing.

What Is Colour Psychology?

Colour psychology is an established area of scientific research. It analyses the effect of colours on human behaviour, mood, cognition, and other mental processes.

No product goes public without checking which colour might enhance sales. Big companies think about complete colour schemes to design their brand. They paint the workplace for their employees, depending on whether they want them to be calm, creative, or alert. It’s all psychology!

Colour combinations are interesting, too. Think of fast-food restaurants. Why do they all use red and yellow in their logos? Because red alone is considered to attract spontaneous buyers, while red and yellow together stimulate hunger. Remember that next time you suddenly crave a burger!

Colours can do so much more. Early references date back to 2000 BC when colours were used as therapy in ancient Egypt and China. A German castle I once visited had a bedroom painted in blue because the colour supposedly helped the duke with his severe eye problems.

In the early 20th century, psychologist Carl Jung was among the first to examine colours and their meanings scientifically. His writings are the foundation of modern colour psychology.

The Effects Of Colour

Colour Psychology is a complicated science. There are many studies still being conducted on how the light of a certain colour goes through our eyes and then causes hormones to respond. To give you a basic idea, please look at this infographic.

How To Use Colour Psychology In Your Writing
Image: Susanne Bennett, 12 January 2023
All colours have two aspects.
  1. Yes, red is the colour of passion and energy. It is also a colour that warns us of life-threatening danger. No wonder a stop sign is red!
  2. Orange and yellow are positive and optimistic colours. They’re also attention-grabbing. Hence construction workers wear orange vests and taxis are often yellow.
  3. Blue and green are soothing colours. They’re also not considered very exciting. Green is the colour of growth and hope, but you could also see it as symbolising incompleteness.

Why Should Writers Apply Colour Psychology?

Colour psychology works in a very subtle, subconscious way. It’s a perfect tool to set a scene by creating a certain tone and mood. This makes the story attractive to readers.

Colours also infuse meaning. Hollywood movies have been using that effect for decades! Just think about the Star Wars movies. Luke Skywalker wears white most of the time, while Darth Vader is the villain in black. That’s the classic colour scheme. But it’s broken up once Luke has discovered his father. He returns to his friends suddenly wearing a black Jedi uniform. Why? To make it more interesting to the audience. Because now Luke realises he has both sides of the force in him, good and bad. And the story has a new twist.

In Hollywood, directors and producers choose their palettes before preproduction. Nothing is left to chance!

Some Examples From Fiction

Writers have been using colour psychology for centuries to steer the reader’s attention and emotions. Don’t believe me?

  1. Let’s look at an American classic, Gone With the Wind by Margaret Mitchell. The main character is ‘Scarlett O’Hara‘. Colour psychology teaches us that anyone with that name should be a fiery, strong woman. The colour of her eyes even heightens the visual impression. They’re green, the complementary colour to red. Let’s remember that green is also associated with deviousness and envy. Right from the start we know that Scarlett is ambiguous. She has all it takes to be a great literary hero.
  2. In The Great Gatsby, F. Scott Fitzgerald mentions Jay Gatsby’s green lawn, the ivy on his house, and the green leather interior of his car. Here, green is the colour of wealth (remember, Americans call the dollar ‘greenback’). Green gains another aspect. Jay mentions that his love interest Daisy has a green light burning at the end of her dock across the lake. Jay tries to reach it but fails, just as he does not accomplish a relationship with Daisy. Green here symbolises the other side of hope, the continuing effort without accomplishment. Can you see how you can use the colour symbolism in a manifold way?

Tips On Using Colour Psychology In Writing

Please make conscious and informed choices. Hopefully, the infographic will help you with this. In addition, here are a few more tips.

  1. Be aware of the attention-grabbing colours red, orange, and yellow. Use them wisely and sparsely. Don’t lead your reader astray by using these colours for unimportant small things or characters.
  2. Mention the colour in different shades to avoid repetition. ‘Red’ can be scarlet, vermillion, cherry, rose, merlot, berry, and many others. Choosing the right hue of colour adds meaning (scarlet just isn’t the same as rosy, right?). If you need inspiration, we have a nifty article on 204 Words That Describe Colour in general.
  3. Mention things that naturally have the colour you want, so you don’t have to explicitly name them. Readers are intelligent enough to know that a lawn and ivy are green. It’s ‘show don’t tell’ for colours.
  4. To create round characters, remember that colours have positive and negative meanings. Choose your colour scheme wisely.
  5. Be aware that the meanings of colours can vary in other cultures. For example, money is green in the USA but not in England, France, and Germany. Their banknotes tend to be more colourful. Another example is the colour white which symbolizes innocence and purity and is associated with weddings. In Eastern Asia, white is the traditional colour for mourning.

The Last Word

I hope this article encourages you to use colours in your writing. Think about what each colour suggests and how it supports your story. A small detail can reveal a great deal. It’s an incredible tool to enhance the quality and meaning of your stories and poems. You may also enjoy reading: How To Judge A Book By Its Colour

Photo by Lucas George Wendt on Unsplash

Susanne Bennett
By Susanne Bennett. Susanne  is a German-American writer who is a journalist by trade and a writer by heart. After years of working at German public radio and an online news portal, she has decided to accept challenges by Deadlines for Writers. Currently she is writing her first novel with them. She is known for overweight purses and carrying a novel everywhere. Follow her on Facebook.

More Posts From Susanne
  1. How To Know When To Quit Writing
  2. Got The Blues? Write A Blues Poem!
  3. What Is Automatic Writing?
  4. How To Write An Elevator Pitch For Your Novel
  5. What Is A Pastiche & Why Should You Write One?
  6. What Is Steampunk? How To Write Steampunk Fiction
  7. How To Write A Space Opera
  8. What Is Dystopian Fiction & How Do I Write It?
  9. What Is Utopian Fiction & How Do I Write It?
  10. How To Write Alternate History: A Complete Guide For Writers

Top Tip: Sign up for our free daily writing links.

The post How To Use Colour Psychology In Your Writing appeared first on Writers Write.

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

Speech models in Ollama

1 Share
Ollama now supports speech models, starting with Phonon from Gradium. Read text aloud in five languages and dozens of voices, streamed as it is generated, over 100 times faster than real time on Apple Silicon.
Read the whole story
alvinashcraft
35 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Android Weekly Issue #750

1 Share
Articles & Tutorials
Sponsored
75% of mobile EMs surveyed require an extra review of AI code before it ships. As AI writes more of each release, reviewers either become the bottleneck or turn into a rubber stamp. Leaders from Thomson Reuters, Mercado Libre, and Salesforce discuss how they’re handling it. Live Nov 12, 1pm ET.
alt
rayford examines how to monitor Compose recomposition triggers by injecting instrumentation at compile time with ASM.
Katie Barnett shows how to implement Android AppFunctions, letting apps act as on-device MCP servers.
Sponsored
Anbox Cloud 1.30 adds fully virtualized Android. Bring AOSP, AAOS, or Cuttlefish images, scale GPU-accelerated instances on demand, and run CTS/VTS with 24/7 support. No physical devices or emulators needed. To stream and test apps, run containerized Android on Anbox Cloud across Arm and x86.
alt
KMP Bits argues shape tokens should be named by role, generating matching Compose and SwiftUI shapes from one file.
Ajesh Pai, Nguyen Van Minh and Tracy Agyemang outline how Grab cut ANRs with R8 and Baseline Profiles.
Pankaj Rai explores multi-agent teams for on-device Android AI, adding supervisor routing and approval safety gates.
Jesse Wilson shares a workaround for leaked certificate authority keys affecting OkHttp users until patched releases arrive.
Steven Rogers shares lessons from shipping a running app with Kotlin Multiplatform, Compose, and native watch code.
Kevin Schildhorn explains how to avoid flaky Kotlin flow tests by controlling coroutine dispatchers and schedulers.
Ravi Rawal examines whether DTO, domain, and UI model mapping layers in Clean Architecture are worth their cost.
Sai Ram Kotha examines how Gemini Nano's 4,000-token input limit shapes on-device Android prompt budgets.
Kevin Desai walks through shipping a 584 MB Gemma model via Play for On-Device AI.
Rushi Patel explains how Xiaomi, Oppo, Vivo and Realme battery savers block pushes and what developers can change.
Joe Birch shows how to morph a floating action button into a dialog in Compose Multiplatform.
Manuel Alejandro Hermoso outlines 13 small, well-described Hacktoberfest contribution issues for Telar, a Kotlin AI-agent library.
Mayank Kumar examines why passing AI-generated Android tests does not prove an app works on real devices.
Place a sponsored post
We reach out to more than 80k Android developers around the world, every week, through our email newsletter and social media channels. Advertise your Android development related service or product!
alt
Libraries & Code
A floating on-screen log that shows Android analytics events live on the device, so testers need no logcat.
alt
A Compose compiler plugin that logs why composables recomposed, naming each changed parameter or state value.
A Compose Multiplatform library that renders your app inside simulated device frames for web previews.
A Compose Multiplatform library that morphs a floating action button into an alert dialog.
A Kotlin app that debloats Android TVs and transfers files from your phone, with no root or PC.
News
Google announces apps must use contact intents instead of WRITE_CONTACTS, with MANAGE_CONTACTS arriving in Android 17 QPR2.
Videos & Podcasts
Learn how Airbnb engineers build fluid, battery-friendly UI motion and text animation.
Firebase shows an agent porting an Android book-Q&A app to iOS, testing whether it succeeds.
Philipp Lackner shows how AI-generated quizzes in pull requests help reviewers understand AI-written code, integrated into CI.
Read the whole story
alvinashcraft
48 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

More AI Agents, More CI: Reducing GitHub Actions Costs & Build Time

1 Share
AI assistants send code through tangled build pipelines into a routing switch that directs necessary work to desktop, laptop, and mobile builds.

AI coding agents are pretty awesome at helping me move projects forward. They can investigate an issue, make a change, and get a pull request ready while I am working on something else. But there is another part of that workflow that has to keep up: CI.

More agents means more branches, more pull requests, more updates to those pull requests, and more GitHub Actions jobs. Add Windows, Android, iOS, macOS, x64, and ARM64 into the mix, and one small change can turn into a whole lot of build machines doing work.

I could see it in my own usage throughout the year. The chart for my top repositories kept climbing, with a particularly noticeable jump toward the fall. That is the value of the Actions usage, not what I actually paid: much of it is included or covered by GitHub's free usage rules. Still, the growing compute footprint was a good reason to take a closer look.

Qualitative sketch of the supplied January-to-October repository usage chart. Several anonymized repository lines rise over the year, with the largest increases toward August through October. Dollar values and exact proportions are intentionally omitted; these are gross usage trends, not net billed charges.
The trend that prompted the audit. Qualitative redrawing of my top-repository usage chart, with amounts, exact proportions, and repository identities omitted. Gross usage value is not the amount billed; October is a partial month.

That was the problem I wanted to dig into across my apps. Not "how do I stop testing things?" Instead: how do I stop paying for, and waiting on, work that doesn't need to happen?

I used the GitHub Copilot app to analyze my GitHub usage export, inspect the workflows, and work through changes across repositories. Some wins were straightforward. One of the most useful discoveries was deleting caches we had just tried adding. That is right: a cache hit is not automatically a performance improvement!

The build matrix is a multiplier

Let's say an agent pushes three updates to a pull request, and each update starts four platform jobs. That is twelve jobs, before retries, release validation, or another agent working on another feature.

Parallel execution makes this easy to miss. Four five-minute jobs might finish in roughly five minutes if they start together, but they still consume about twenty runner-minutes. Queueing can make the wait longer without adding the same amount of execution time. A release waiting for approval is another thing entirely.

Those are different measurements:

Measurement What I want to learn
Runner execution time How much compute did the jobs actually use?
Pull request feedback time How long until I can act on a useful result?
Billed usage What remains chargeable after GitHub's billing rules and allowances?

The goal is to improve the right number, not just make a workflow page look shorter.

Start with the export, then follow it into the logs

My first prompt in the GitHub Copilot app was:

can you do an analysis of my actions across my repos running on windows and mac runners and see where we can optimize and cut costs

Then I attached the GitHub summarized usage CSV. My next message was simply:

Herei sm y usage report

Yes, typos and all. The useful part was supplying the data, not writing a magical prompt.

We looked at an October 1-24, 2026 snapshot containing 1,100 export records, reconciled it with GitHub's live billing API, and inspected 66 workflows across 14 repositories associated with Windows, macOS, or paid Actions usage. We also fetched 748 job records, not 748 independent workflow runs. That was complete for two paid hotspots, with selected representative runs elsewhere, not a complete performance census of every repository.

The export told us where to look. Workflow files and job logs told us what to change.

We separated setup, restore, compilation, tests, packaging, and cache transfer. We also checked whether a job really needed that operating system, whether superseded runs were still working, and whether "nothing changed" checks were allocating a native runner just to exit.

If your export includes gross usage, discounts, and net charges, keep those separate. An avoided dollar of nominal compute is not necessarily a dollar off your bill. I am deliberately not sharing my billing totals or repository-level billing breakdown here.

One more important limitation: a busy partial month is not a reliable annual forecast. It is an excellent place to find waste, though.

Put each job on the runner it actually needs

First things first: an Android build does not automatically need a Mac just because the same application also targets iOS.

For my FIRE calculator Android workflow, we moved from macos-15 to ubuntu-24.04, installed the Android-specific .NET MAUI workload, and configured the required JDK and Android SDK dependencies. We kept the required check name, tests, signing configuration, artifacts, and gates.

The hosted Android job completed in 2 minutes 35 seconds with all 447 tests passing. That confirms the Linux path worked; it is not a matched before-and-after speed benchmark. The signed AAB credential path was not exercised, and we did not dispatch a release to prove it.

Why move it? Here are the relevant GitHub standard runner list rates, checked October 24, 2026:

Runner billing SKU USD per minute Relative to Linux x64
Linux x64, 2-core $0.006 1.00x
Windows x64, 2-core $0.010 1.67x
macOS, M1 or Intel $0.062 10.33x
Published per-minute rates normalized to Linux x64 at 1: Windows x64 is 1.67 and macOS is 10.33.
Published list-rate comparison, not my bill or a hardware-performance comparison. Runner capabilities and runtimes differ.

At equal billed runtime, moving eligible work from that macOS SKU to Linux x64 reduces its nominal compute price by about 90.3%. That is a rate comparison, not a measured reduction in my total bill.

In fact, this example is a public repository: standard GitHub-hosted runners are free for public repositories. For private repositories, included allowances and discounts affect marginal savings. Larger runners have different rules and are not free just because a repository is public. GitHub also rounds each job's partial minutes up to whole minutes.

The practical lesson is still useful: keep Apple builds on a compatible Mac, Windows-specific packaging on Windows, and move portable work only after proving it works elsewhere.

Don't start an expensive machine to say "nothing changed"

Another discovery was in My Stream Timer. Its required Core test and x64/ARM64 matrix checks still needed to report results, including when a change did not affect the app.

Simply skipping an entire workflow is not a safe substitute for understanding your branch rules. Required checks can end up waiting for a result that never arrives.

Instead, we kept the check names and matrix shape. No-change checks run on Ubuntu; actual Windows work still runs on Windows.

A change detector routes unchanged app inputs to a Linux no-op required check and changed inputs to Windows builds and tests. Detection errors fail rather than reporting success.
Keep the required-check contract. Route the work, not the truth of the result.

There is a gotcha here: a failed change detector must not become a successful no-op. We checked invalid outputs, upstream failures, and cancellation as well as the happy path. Hosted Windows packages for both architectures, tests, and package validation passed. Seven live routing fixtures exercised the no-change and failure/cancellation paths.

This proves the routing behavior. We did not collect a controlled per-run latency benchmark, so I am not attaching a made-up number to it.

Cancel superseded CI, not releases

When an agent pushes a fix while the previous build is still running, do you need both ordinary CI runs to finish?

Usually, I want feedback for the newest commit. Workflow- and pull-request-scoped concurrency lets us stop spending time on an obsolete revision without having different workflows cancel each other.

Here is a small illustrative example for ordinary pull request CI, not a drop-in release policy:

on:
  pull_request:

concurrency:
  group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
  cancel-in-progress: true

We added scoped cancellation to ordinary build workflows in Tiny Clips and OutroKit. Release publishing and deployment workflows were not the target.

Cancellation cannot refund work that already ran. Its payoff depends on how often updates overlap and how much execution remains when the new commit arrives. Measure that instead of assuming every canceled run saves its full duration.

Measure the cache, including moving the cache

This was my favorite part of the investigation.

In Tiny Clips, we tried separate NuGet download caches for direct x64 builds, direct ARM64 builds, Store builds, and tests. That sounds pretty reasonable. However, downloading and unpacking a cache takes time too.

For the direct builds, warm cache-plus-restore paths were 16 seconds slower for x64 and 10 seconds slower for ARM64 than direct restore without the cache. Even the Store cache, which improved over its cold-fill path, was about seven seconds slower warm than restore alone.

We removed all three build caches.

The test cache was different:

Test dependency setup path Observed time
Restore without the cache 32 seconds
Cold cache path, including its save 44 seconds
Warm cache path, including cache overhead 21 seconds
Tiny Clips test setup took 32 seconds without a cache, 44 seconds on the cold cache path, and 21 seconds warm. Warm saved 11 seconds versus no cache.
Observed setup timings, not total workflow durations. One cold/warm pair is useful evidence, not a repeated controlled benchmark.

That is an 11-second warm setup improvement, about 34% of this setup phase. Not a 34% faster application build! The retained test cache was about 0.45 GiB, compared with roughly 3.80 GiB across the initial cache experiment.

The decision rule is pretty simple:

Compare cache lookup, download, extraction, remaining restore, and post-job save against the no-cache restore. A green "cache hit" message is not the finish line.

We kept full platform coverage. Tiny Clips validation included Windows packages for both architectures and the Store flavor, 2,388 Core test cases with zero failures and four skips, 218 passing App tests, plus 398 Mac tests and a Mac App Store build.

In OutroKit, we retained fresh restores in each job and scoped NuGet download caches by OS, architecture, and dependencies. The full Linux, Windows, Intel Mac, and Apple Silicon Mac test matrix remained. All six jobs passed, and the pack job reused the exact build cache. That establishes correctness and reuse, not a controlled speedup measurement.

Follow-up: where does the Windows cache live?

After that experiment, Awake Coding's post on Windows .NET builds and NuGet cache placement gave us another thing to measure: where NuGet reads and writes locally, not whether to download an archive from actions/cache. Those are separate decisions!

We ran four fresh GitHub-hosted Windows x64 jobs each for Tiny Clips and my file converter: two default-location jobs and two with NuGet's global packages and HTTP caches under RUNNER_TEMP. Both caches started empty; remote Actions cache transfer was off. Within each app, source, SDK, workspace, toolchain, and commands stayed the same. We tested both cache paths together, not each variable's contribution independently.

Default caches were on C:, while temporary files and the workspace were on D:. All jobs used the same Windows image, but one temporary-location job in each pilot landed on newer EPYC 9V45/NVMe hardware, versus EPYC 7763/SAS for the other three. The faster-hardware jobs are excluded below. These are independent machines in the same broad CPU/storage class, not repeated paired lab measurements.

Cold-cache pilot Default restore, two jobs Temporary-location restore, one comparable-class job Whole experimental job, default → temporary
Tiny Clips 51-56 seconds 13 seconds 227-237 → 183 seconds
File converter 53-61 seconds 10 seconds 357-368 → 259 seconds

That is a promising cold-restore signal across WinUI and MAUI! Other phases barely changed: Tiny Clips' build/package step went from 138-144 seconds to 137; the converter's compilation from 48-52 to 46. Converter workload installation stayed around 20-22 seconds and tests around 41-43. This is separate from the isolated-SDK improvement described below; that setup was unchanged between the converter arms.

All four Tiny Clips jobs passed direct x64 build and MSIX inspection; this pilot did not run tests or ARM64 builds. All four converter jobs passed the Windows app build, native x64/ARM64 checks, and 15 Bridge plus 297 Core tests, with three expected Apple-only skips per job. Neither pilot exercised Store packaging or release/signing paths. Whole-job times include provisioning and diagnostics, not a promise of production CI duration. The Tiny Clips report and converter report preserve the experiment details.

Here is an illustrative GitHub-hosted Windows step, before SDK setup, remote cache restore, or any NuGet-consuming command:

- name: Put NuGet caches under runner temp
  if: runner.os == 'Windows' && runner.environment == 'github-hosted'
  shell: pwsh
  run: |
    $packages = Join-Path $env:RUNNER_TEMP 'nuget-packages'
    $http = Join-Path $env:RUNNER_TEMP 'nuget-http'
    New-Item -ItemType Directory -Force -Path $packages, $http | Out-Null
    "NUGET_PACKAGES=$packages" >> $env:GITHUB_ENV
    "NUGET_HTTP_CACHE_PATH=$http" >> $env:GITHUB_ENV

Verify your actual paths and disks first: RUNNER_TEMP does not guarantee a faster D: drive, especially on self-hosted machines. Preserve existing deliberate local/NVMe cache placement. If you also use actions/cache, point its package path at ${{ env.NUGET_PACKAGES }} and version the key when changing the cache layout, retaining OS, architecture, and dependency scoping. Do not remotely persist the HTTP cache or credential paths.

Faster feedback is not automatically a smaller bill. The comparable Tiny Clips jobs all round up to four runner-minutes; the converter observations round from six/seven to five. These public repositories use free standard hosted compute, so neither is a measured dollar saving. These pilots did not change production workflows. Tiny Clips' retained test cache was already on D:, and the converter uses a remote package cache in production: neither warm-path benefit was measured here.

My takeaway: try local placement for cold restores, then measure the whole job and any remote transfer separately. It does not overturn the decision to remove slower remote build caches, and these pilots are not included in the cost estimate below.

Fix the environment, not just the cache

Across other cross-platform apps, Windows MAUI workload installation was an obvious hotspot: roughly six and a half to seven minutes in representative jobs.

Tempting solution? Cache the workload folders and skip installation.

Not so fast. The logs showed MSI-managed components and machine-wide Windows Installer state. Restoring a directory is not equivalent to installing that toolchain correctly. We deferred workload-folder caching rather than trade a slow reliable build for a fast mysterious one.

But that did not mean giving up on setup. In my file converter, the preinstalled SDK was reconciling unrelated Visual Studio Android, Apple, and WebAssembly workloads when we only wanted maui-windows. Instead of caching that machine-wide state, we used actions/setup-dotnet with DOTNET_INSTALL_DIR pointing to a fresh directory under runner.temp. The isolated SDK uses file-based workload installation; we still install and verify the pinned workload explicitly. No registry edits, deleting installer markers, or pretending a folder restore is an installation.

The observed Windows workload-install step went from 5 minutes 55 seconds to 20 seconds, and the whole job from 10 minutes 39 seconds to 3 minutes 48 seconds. All seven hosted checks passed, including the Windows app build and native shell checks. These are observations across workflow revisions, not a controlled benchmark attributing every second to SDK isolation; Store packaging was not dispatched.

Observed Windows timings across workflow revisions: workload installation fell from 5 minutes 55 seconds to 20 seconds; whole job duration fell from 10 minutes 39 seconds to 3 minutes 48 seconds. Installation is part of the job, not an additional saving.
Observed hosted runs, not a controlled benchmark. Workload installation is included in total job time; do not add the two reductions together. Store packaging was not exercised.

Some optimizations should stay on the drawing board

Apple pipelines had a similar lesson: a small test body can sit inside a much longer compile, install, simulator startup, and test operation. Profile the whole operation before deciding that deleting tests is the answer.

In one iOS cache experiment, the warm run avoided recompiling unchanged Swift files and cut a portable test step from about two minutes to 17 seconds. Awesome, right? Except the whole workflow went from 11 minutes 15 seconds cold to 14 minutes 53 seconds warm. Setup and native-test overhead outweighed the compilation win. One pair is not a controlled benchmark, but it certainly does not justify claiming overall savings.

We rejected that iOS cache experiment rather than keep the overhead and complexity. The final proposal keeps earlier simulator startup and disables Homebrew's automatic update/cleanup, with all 52 tests preserved. Its uncached run took 11 minutes 27 seconds, near the audit median, so I am counting zero overall savings from it. Source-built media caching in my file converter also has no verified hosted warm-hit benefit yet.

For the converter, I also explicitly chose faster automatic PR checks with full Mac proof on demand. The initial full Mac job took 9 minutes 17 seconds; compile-only checks took 3 minutes 10 seconds in one run and 4 minutes 15 seconds in the latest. Those are observational timings on differing runner images, and this is an intentional change in coverage frequency, not a cache speedup or identical work running faster. Automatic Mac compilation/static guards remain; the manual media, sandbox, and runtime proof must run for the exact release candidate before Store approval. Store signing and approval boundaries stay separate.

The media cache also correctly missed when the runner image changed. That is a safety check doing its job, not evidence of a warm-cache speedup. A reusable media cache must contain verified unsigned source-built outputs, never signing credentials or signed app bundles.

The prompts were a starting point, not the quality gate

After the analysis, my next actual prompt was:

Let's send PRs for all of these please with optimizatons

In the GitHub Copilot app, that became separate, isolated coding sessions for the repositories. Each could inspect its own workflow, make changes, and bring back a pull request and hosted CI evidence. The useful part was being able to coordinate the results: keep the good cache, remove the slower ones, preserve the required checks, and challenge optimizations that were not safe.

For a repeatable version of this process, here are suggested follow-up prompts, not quotations from my original conversation:

Analyze this GitHub Actions usage export alongside the workflows
and representative job logs. Separate runner execution time,
feedback latency, gross usage, discounts, and net billed charges.
Identify the highest-impact changes and the evidence for each.
Do not publish the raw export or private billing details.
For each proposed optimization, preserve required check names,
platform coverage, signing boundaries, and release gates unless
I explicitly approve a behavior change. Explain what can run on
Linux, what must stay native, and how failures propagate.
Measure no-cache, cold-cache, and warm-cache setup, including
lookup, transfer, extraction, restore, and post-job save time.
Remove caches that do not beat the no-cache path. Report sample
size and limitations; do not claim overall workflow speedups
from setup-only measurements.
Validate with hosted CI and report exactly what passed, what was
not exercised, and whether the PR is open or merged. Do not
dispatch a signed release just to test an optimization.

I like this approach because the agent is doing the investigation and implementation, but the acceptance criteria are still concrete. "It should be faster" is not enough.

So, how much time and money does this save?

Putting the changes together, a reasonable planning scenario is roughly 45-51% less nominal compute cost and 10-20% fewer runner-minutes for the example workload below. That combines cheaper runners, less redundant execution, and the test cache that actually helped. It is an estimate informed by our tests, not a measured reduction across my account.

Illustrative combined estimate, with the original workload indexed to 100: nominal compute cost falls to about 49-55, and runner execution time falls to about 80-90. This models runner migration, avoided redundant execution, and test setup caching; it is not measured account savings or pull request latency.
Combined planning scenario, not my actual workload or bill. Lower is better; baseline = 100. Simulator/media-cache gains, Windows SDK isolation, and on-demand Mac proof are excluded from this model.

The assumptions: 100 five-minute jobs split 40 Linux, 20 Windows, and 40 Mac; move 20 eligible Mac jobs to Linux at equal runtime, then avoid 10-20% of execution proportionally through cancellation/no-change routing, net of routing overhead. Twenty of the original Linux jobs also get the observed test-setup cache benefit, assuming 80% warm hits and including cold-cache penalties, scaled down with the avoided work. That works out to about 52-102 runner-minutes saved per 100 jobs. The job mix and avoidance rate are illustrative assumptions, not measurements of my fleet.

Actual billed savings depend on free usage, allowances, and per-job rounding, which this approximate model does not calculate. Less total execution also does not mean every PR finishes 10-20% sooner. Still, it gives us a useful target to compare with the next usage report, without counting unproven optimizations as wins!

Make the next build smarter

The big takeaway for me is not to use fewer agents or stop building cross-platform apps. It is to make the CI graph as intentional as the code those agents are changing.

Start with your usage export. Follow the expensive work into job logs. Put portable work on the right runner, stop obsolete builds, keep required checks truthful, and make caches earn their place.

Then let the agents help with the cleanup, and ask them to bring back the evidence. More pull requests do not have to mean proportionally more wasted CI.

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

F# Weekly #43, 2026 –Fable 5.24 and Ionide 8

1 Share

Welcome to F# Weekly,

A roundup of F# content from this past week:

Microsoft News

Videos

Blogs

Highlighted projects

Fable 5.22 is out. Since 5.16:- Opt-in Temporal dates / times for JS- Implicit conversion to U2..U9- Cold builds: project cracking 7.7s to 2.3s 🏎- Compilation up to 2.9x faster (server GC + JIT tuning) 🎉- Faster watch: incremental inline exprs- Many BEAM and Rust fixes#fsharp #fablecompiler

— Maxime (@mangelmaxime.bsky.social) 2026-10-20T09:11:44.122Z

New Releases

A Free .NET Profiler for VS Code: CPU, Memory and Leaks in C# and F#buff.ly/Et9QscC#dotnet #vscode #csharp #fsharp #profiler #debugging #devtools

— Alvin Ashcraft (@alvinashcraft.com) 2026-10-19T18:01:22.680Z

That’s all for now. Have a great week.

Buy Me A Coffee





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