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

Building a Live Blog Post Editor Canvas with GitHub Copilot

1 Share
The Post Workbench canvas showing this blog post's Markdown on the left and its live preview with screenshots on the right.

Ever since I rebuilt this blog as a static Hugo site with GitHub Copilot CLI, every post is just a Markdown file in a git repo. I love that! But there was one thing that kept bugging me: to see what a post actually looks like, I had to start the whole site with hugo server, wait for it to build, find the post, and then go back and forth between my editor and the browser. And I wanted to do that before sending a pull request, not after the preview deployment came back.

What I really wanted was an elegant little place to pick a post, edit it, and watch it render with all of its images, videos, and embeds. It didn't need to look exactly like the site. It just needed to show me the content on a light or dark background, so I could catch a broken image or a wonky screenshot before anyone else did.

So I asked Copilot to build it for me as a canvas, and this post is the story of how that went. Oh, and every screenshot in this post is of the canvas editing... this post. 🤯

The Post Workbench canvas showing this post's Markdown on the left and its live preview on the right.
Post Workbench editing this post in a split view.

What is a canvas anyway?

A canvas is a panel that a Copilot extension can open right next to your chat. It is plain web content: the extension starts a small local web server, hands the host a URL, and the host shows it in a panel. The extension can also give the agent actions, so Copilot can drive the canvas for you ("open my newest post", "switch to dark mode").

Extensions can live in a few places. I picked workspace scope, which means the canvas lives in my blog repo at .github/extensions/post-workbench/. That has a really nice side effect: the canvas always works on the checkout that contains it, so every branch or worktree sees its own posts, its own edits, and its own git status. That is exactly what I want when I am working on a post in a branch before a PR.

Building it with the create-canvas skill

I started with the create-canvas skill and this (typo-filled, as usual) prompt:

i would like to create a canvas for visualizing our posts, and and allow liveing editing and previeing in the current branch. i want to be able to visualize the images/videos and such like that. it doesn't need to be actually what it woudl look like on the site jsut a preview on a light or dark backgroudn (with toggle)

Just like with my site migration, the best thing I did was let Copilot plan first. It went off and explored the repo: how my posts are laid out, how Hugo mounts static/ and static-case/ for images, which shortcodes I use for YouTube, Vimeo, Fireside, and gists, and how the canvas SDK actually works. Then it asked me a few questions before writing any code:

  • Where should it live? Workspace scope, so it ships with the repo.
  • How should Markdown be rendered? A vendored copy of marked, so there is no npm install and no build step.
  • Should it save automatically? Yes! Autosave on by default, with the option to turn it off.

From there it wrote a plan with 16 todos, I approved it, and it got to work.

Taking a tour

Let's take a look at what came out the other side.

All of my posts, at a glance

The canvas opens on a grid of every post with its cover image, date, tags, and how many images and videos it has. I can search, filter to drafts, or filter to Changed, which shows only the posts that differ from my main branch or have uncommitted edits. That one is basically "what am I about to put in this PR?"

The Post Workbench post list showing a grid of blog post cards with cover images, dates, tags, and media counts.
Every post in the repo, newest first.

Edit and preview side by side

Pick a post and you get the Markdown on one side and a live preview on the other. As I type, the preview updates, and with Sync scroll on, both sides stay together. Autosave writes the file a moment after I stop typing, and it keeps the file's line endings intact so my diffs stay clean.

Split view with the Markdown source of this post on the left and the rendered preview with a screenshot on the right, on a light background.
Split view on a light background.

Here is the toggle I really wanted: one click and the preview flips to a dark background. This is super handy for screenshots with transparent backgrounds or light borders that look great in one theme and disappear in the other. There is even a Transparency option that puts a checkerboard behind transparent images.

The same split view with the preview on a dark background.
Same post, dark background.

Every image, video, and embed

The Media tab lists every image, video, audio file, and embed in the post, with its source line and a status: found, missing, too large, or a case mismatch. That last one is sneaky: Windows doesn't care if your file is Screenshot.png and your post says screenshot.png, but the Linux build on GitHub Actions absolutely does!

The Media tab listing this post's screenshots as tiles, each with a found status and a line number.
The Media tab checks every reference before the build does.

Click any tile to open it in a larger viewer with its size and file details, and jump straight to the line in the source where it is used. YouTube, Vimeo, and Fireside embeds show a card first, and only load the player when you click Load player.

A screenshot opened in the Post Workbench media viewer, with its dimensions, file size, and a Show in source button.
The media viewer, with a quick way back to the source line.

Copilot can drive it, too

Because the canvas exposes actions, Copilot can use it without me touching the panel. I can ask it to list the posts I changed on this branch, open a specific post at a line, check a post's media for problems, or switch the preview to dark. That makes "hey, go check the images in my new post" a real request.

For example, here are the results when Copilot calls get_post_media on this post: every image comes back as found, with its line number, file size, and git status. Then it calls open_post with a line number and set_preview_theme with dark, and the canvas jumps right there:

The Post Workbench canvas on a dark background, scrolled to the A peek under the hood section of this post after Copilot opened it at that line.
The canvas after Copilot opened this post at a line and switched the preview to dark.

A peek under the hood

The canvas is just an ES module named extension.mjs that uses the Copilot SDK. Here is the shape of it, trimmed down to one action:

import { CanvasError, createCanvas, joinSession } from "@github/copilot-sdk/extension";

const canvas = createCanvas({
  id: "montemagno-post-workbench",
  displayName: "Post Workbench",
  description: "Browse, preview and live-edit this blog's Markdown posts.",
  actions: [
    {
      name: "set_preview_theme",
      description: "Switch the preview background between light, dark, or the system setting.",
      inputSchema: {
        type: "object",
        properties: { theme: { type: "string", enum: ["light", "dark", "system"] } },
        required: ["theme"],
      },
      handler: async (ctx) => registry.require(ctx.instanceId).actions.setPreviewTheme(ctx.input),
    },
  ],
  // Start (or reuse) a local server for this panel and give the host its URL.
  open: async (ctx) => registry.open(ctx.instanceId, ctx.input),
  onClose: async (ctx) => registry.close(ctx.instanceId),
});

await joinSession({ canvases: [canvas] });

open starts a small web server on 127.0.0.1 with a random port and returns its URL, and calling it again for the same panel reuses that server, so my unsaved edits survive. There is no package.json and nothing to install: the runtime provides the SDK.

Everything else lives in sibling folders: a Node side in lib/ that indexes posts, reads git status, and saves files, and a plain JavaScript browser app in web/ with no build step. A few details I really appreciated:

  • Saves are safe. Files are written to a temporary file and then swapped in, so a crash can't leave half a post. If the file changes on disk while I have unsaved edits (say, Copilot edited it in chat), I get to choose which version wins instead of silently losing one.
  • Post content is treated as untrusted. The preview is rebuilt from an allowlist of safe HTML, a content security policy backs that up, and embeds run in sandboxed frames.
  • Preferences stay out of the repo. Things like the light or dark choice are stored under my Copilot home folder, not in git.

One gotcha worth knowing if you build your own: never use console.log in an extension. Standard output is how the extension talks to the host, so stray logging can break the connection. Use session.log() instead.

Testing it for real

This is where working with an agent really shines. It didn't stop at "it compiles." Copilot wrote more than 140 tests for the Node side and the rendering, ran them on Node 20, 22, and 26, and then drove the canvas in a real browser to check editing, autosave, conflicts, keyboard navigation, narrow panels, and dark mode. It even rendered all of my posts in the preview to make sure none of them broke it.

That testing caught real bugs. The Skip to content link was quietly closing the open post, because the canvas uses the URL hash for navigation. A test that passed on my machine flaked under load because of a timing race. And the test command itself behaved differently on Node 20 and Node 22.

Then I had Copilot run a separate code review, and it found the scariest one of all: if a post briefly disappeared or couldn't be read (think git stash, switching branches, or Windows holding a lock on the file), the canvas treated it as deleted and threw away my unsaved edits. Yikes! Now only a file that is truly gone counts as removed, the edits are kept as a draft, and editing picks right back up when the file comes back. All of these got fixed before I ever hit them, which is exactly what I want from my AI pair programmer.

Try it or build your own

If you have a Markdown blog or docs site in a repo, this is a fun one to build for yourself. Start with the create-canvas skill, tell it what you want to see and edit, and let it plan before it codes. Keep it in your repo with workspace scope, so it follows whatever branch you're on.

As for this post, it was previewed and checked in the canvas, right next to my chat, with no hugo server in sight. That is exactly the workflow I was after before sending a PR!

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

How I Got My Cadence Ready for iPhone Duo (Without Redesigning It)

1 Share
Close-up of the live cadence chart in My Cadence, a red, orange, and yellow line rising and falling across dashed RPM guide lines marked 94, 85, and 73.

My Cadence has one job: put a big number on your screen so you can see your RPM while you're on the bike. I built it back in 2020 because Apple Fitness+ wouldn't connect to my cadence sensor, and it has become one of my top apps. These days it's a Swift app on iOS and a .NET MAUI app on Android.

I wanted My Cadence to look and feel great on iPhone Duo. Here is what a ride looked like on the Duo's big inner display before this work:

The old My Cadence layout during a ride on an open iPhone Duo. A small Connected status, a large RPM number, Stop Ride, and four small stats sit across the top of the screen, and the bottom of the screen is empty.
Before: a ride on the open iPhone Duo, captured on the simulator with a simulated ride. This is the old layout rebuilt with today's tools.

It works, but that is a whole lot of empty space! So how do you use all that room without ruining a simple app?

One thing up front: I don't have an iPhone Duo. Everything here was tested on the simulator, and this update isn't in the App Store yet. So, let's walk through the plan, the skills, the design, and the gotchas.

Start with a plan, then find a better one

I started with a written plan, and my first version was ambitious: a new "cockpit" look on every iPhone, built on top of a community toolkit. Then I brought in Apple's iPhone Duo preparation guide, and it changed the whole thing. Apple breaks the work into three steps:

  1. Run the App Resizability skill to scan your project.
  2. Fix what it finds, and inspect what automation misses.
  3. Test resizing on the simulators and tools Apple provides.

One line from that guide really stuck with me: "On the inner display, your app should look like a natural extension of its iPad layout." Keep that one in mind, because it pays off later!

Sticking with Apple's tools

Apple's App Resizability skill comes with Xcode. You can ask Xcode's coding assistant to "get my app ready for iPhone Duo," or export the skill if you use a different agent. I exported it and ran everything from VS Code with GitHub Copilot.

My first plan also pulled in Duology, a community set of skills and a fold linter for iPhone Duo. Once we had Apple's official tooling, I asked whether we still needed any of it, and I decided to stick with Apple's tools only. The most useful part for My Cadence was the checklist, and fewer moving parts felt right for a simple app. If you're building something new for Duo, it's still worth a look.

So what did the audit find? Honestly, not much! My Cadence wasn't sizing itself from the main screen, checking for specific devices, or making layout choices from device orientation. It did turn up the Ride and Scenic player screens choosing their layout by comparing width and height, plus catalog thumbnails that could widen their grid column.

A clean audit isn't the finish line, though. Apple says so in the guide: "The skill finds most issues, but not all of them." Most of the problems in the rest of this post came from reading Apple's docs closely and from actually running the app.

"We don't need crazy colors"

While I was working on the plan, I started thinking about new ideas for what the app could do with all that extra screen. It's hard to picture a layout from a table in a Markdown file, so I asked GitHub Copilot in VS Code to generate some image mockups for me. Here is what came back:

An AI-generated design concept board labeled Ride Cockpit, showing dark teal screens with glowing circular gauges on closed, open, and book-style iPhone Duo devices.
An AI-generated design concept that I rejected. This is not the app.

Pretty wild, right? It's a full "Ride Cockpit" concept, with dark teal screens and a glowing gauge wrapped around the RPM on the closed, open, and book-style Duo. It even came with a tagline: "Same ride. More room."

It's cool, but it isn't My Cadence. The whole point of the app is a big, simple number that you can read while you're riding, and it's supposed to feel like it belongs on your iPhone. So, here is what I said back: "We absolutely want to keep with our very simple design that we have today in the app, we don't need crazy colors or anything like that, we want just super native look and feel for light and dark mode."

Copilot updated the plan and generated a new set of mockups in both light and dark mode. This round looked a lot more like the app I know:

An AI-generated design concept board showing a simple native layout in light and dark mode on closed and open iPhone Duo devices, with a large RPM number, a small cadence line, stats, and a red Stop Ride button.
The revised AI-generated design concept. It's still a mockup, not the running app.

There's the big number, the familiar cards, and a regular Stop Ride button, plus a small, quiet line for live cadence. That became the rule for the project: same app, more room. There are no gauges, gradients, or new color themes, and my existing Pro customizations keep working.

Asking for pictures this early was so useful. No app code had changed yet, so it was really cheap to look at an idea, say "nope," and try again. It's a lot easier to react to an image than to a paragraph describing one!

One thing to be aware of: generated mockups can make things up. These drew approximate system controls and even little camera icons, and that doesn't mean I wanted any of that in the app. I used them to pick a direction, not as a spec. The actual layouts came from Apple's guidance and from running the real app.

"Something much more interesting"

The first working version kept everything simple and added a small gray cadence line. It worked, but with the Duo open I wanted more from all that space. My feedback was that it needed "something much more interesting," like a live chart right alongside the stats.

So now it has one:

My Cadence on an open iPhone Duo during a ride. A connected status and large RPM number fill the left side. Stop Ride, a large live cadence chart, and four stat tiles fill the right side.
After: the open iPhone Duo with the new live chart, captured on the simulator with a simulated ride.

The big chart is the same chart and colors you already see in ride details, and it's free for everyone. The number and stat tiles get bigger too.

Here's the nice side effect: the iPad gets this layout too! I have a lot of iPad users, and they had the same empty screen.

Two iPad screenshots of My Cadence during a ride, side by side. On the left, the old layout has the RPM, Stop Ride, and stats at the top with most of the screen empty. On the right, the new layout has the same RPM and controls, a large live cadence chart filling the middle, and stats at the bottom.
Before on the left, after on the right. Both are real captures on the iPad simulator with a simulated ride. The before is the old layout rebuilt with today's tools.

What about a regular iPhone? It keeps what already worked: the full-size number and the stats, with a small trace added.

My Cadence on a regular iPhone during a ride, with a large RPM number, a red Stop Ride button, a small cadence line, and four stats in a row.
A regular iPhone still looks like My Cadence. Captured on the simulator with a simulated ride.

Light and dark mode come straight from the system, just like they always have:

My Cadence in dark mode on an open iPhone Duo held tall, with the RPM number on top, then Stop Ride, a live cadence chart, and four stats along the bottom.
Dark mode with the Duo open and held tall, captured on the simulator with a simulated ride.

The gotchas

Apple's split container would have hidden the controls

The plan was to use Apple's native layouts first, and for Ride and the Scenic player that meant ArrangementView. Then we read the split style documentation closely: "When someone is holding a closed iPhone Duo in landscape orientation, the split arrangement only shows the primary view."

For My Cadence, that means you'd lose either the number or Start/Stop and your stats. That isn't okay on a bike! We're going by Apple's docs here, since I didn't see that pose for myself before making the call. So we wrote a small layout of our own for those two screens, and History still uses Apple's native split view.

The closed display in landscape needed the help. Here is the old layout, with its cut-off stat labels and a wrapped Stop Ride button:

The old My Cadence layout on a closed iPhone Duo in landscape. The RPM number fills the middle, Stop Ride wraps onto two lines, and the stat labels are cut off to M, R, and TI.
Before: the old layout on the closed Duo, rebuilt with today's tools and running a simulated ride.

And here is the new one, with everything still on screen:

The new My Cadence layout on a closed iPhone Duo in landscape. The RPM number is on the left. Stop Ride, a small cadence line, and four fully labeled stats are on the right.
After: RPM, Stop Ride, sensor status, and all four stats, captured on the simulator with a simulated ride.

My takeaway: go native first, right up until native hides something people need.

The number that got cut off

In an app about one big number, this one hurt. The first pass cut off the RPM on the closed display in portrait. Copilot noticed and made a quick fix at the end of its run, but that fix only stopped the clipping by making the number noticeably smaller.

When I looked at it, I said it should be "more like we have today." The next pass gave the number more of the screen, and it's back to full size:

My Cadence on a closed iPhone Duo in portrait during a ride, with a large uncut RPM number, a red Stop Ride button, a small cadence line, and four stats in a row.
The fixed closed display, captured on the simulator with a simulated ride.

A layout can pass every check and still be wrong for your app. You have to look at it!

Your video might not survive a fold

My Cadence has Scenic rides, which are first-person cycling videos you can ride along with. There were two problems here. The player compared its address against one that always differed, so any view update could reload the video. The screen also swapped between two different layouts, and that destroyed the player whenever it changed.

Now there's one stable player. On the simulator, the video kept playing through a fold, moving from its intro into the ride footage with no new page load. If your app embeds a web player, go test this one! The catalog also uses the room a lot better now, with four columns when the Duo is open instead of one giant card:

The My Cadence Scenic Rides catalog in dark mode on an open iPhone Duo, with a banner, search field, scenery filters, and a row of four video thumbnails.
The Scenic catalog on the open Duo in dark mode, captured on the simulator.

The little ones

A few smaller bugs showed up once we started really testing:

  • A missing Back button. A collapsed History detail had no way back, so selecting a ride with the phone open and then closing it left you stuck. Test that exact path if you use a split view!
  • Ride charts that stopped short. They had a fixed height, so on a big iPad the chart stopped about halfway down the screen. Now they grow to fill the space.
  • "1 rides" and "12 / 1 rides." Yes, really. It had nothing to do with Duo, but my achievements screen now reads "1 ride" and "12 rides."

Here is where History ended up, with the list and details side by side when the Duo is open:

My Cadence ride history on an open iPhone Duo, with a monthly activity chart and ride list on the left and the selected ride's stats and cadence chart on the right.
History on the open Duo with sample rides, captured on the simulator.

Testing without a Duo

Step three of Apple's guide is testing, and the iPhone Duo simulator is Apple's recommended way to test the full experience. One thing to be aware of: you change poses with the Closed, Partially Open, Open, and Rotate controls in Device Hub. Those controls are the only way to do it, so testing takes a while. My Mac even locked in the middle of one run, and that was the end of folding until I got back to it!

The simulator also has no Bluetooth, so we added a debug-only simulated cadence to ride with. With that, one ride kept going from open wide, through book, to open tall.

Wrapping up

If you're getting your own app ready, start with Apple's guide and treat the skill as a great checklist. Then put the app on the simulator and look at it, because the things that mattered most for My Cadence came from looking at it. And don't be surprised if your iPad app gets better along the way.

The update isn't in the App Store yet, but you can check out My Cadence today. It's the same simple app with a whole lot more on screen. Happy riding!

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

5 Mistakes Beginner Writers Make When Creating Characters

1 Share

Learn how to create believable characters and avoid 5 common mistakes that make your fictional people feel flat and unconvincing.

5 Mistakes Beginner Writers Make When Creating Characters

What are the most common mistakes first-time writers make when they create characters? They often forget that they are creating real people.

As Ernest Hemingway said, ‘When writing a novel, a writer should create living people; people, not characters. A character is a caricature.’

After many years of teaching at Writers Write and reading hundreds of unpublished novels, we have found that these are the most common problems beginner writers share when creating characters.

1. Cardboard Cut-Out Characters

Give your characters a life. Surround them with evidence of their past, present, and future. Everyday things that happen to them make them human.

They should argue with their parents, forget a friend’s birthday, and hope everyone will forget theirs. Make them stub their toes, drop their phones in water, and lose their car keys. All of this should happen while they are dealing with other people.

Readers need to see characters as real people. They should be able to recognise something of themselves in them, even when those characters are different from them.

The more human your characters feel, the more believable they become.

2. Overcrowding – Too Many Characters

Don’t give prime space to minor characters. They simply crowd the pages and confuse readers. Readers don’t want to keep track of dozens of characters in a book. Less is always more.

[This also applies when writing memoirs.] Work with four main characters. Make space for two characters who influence and support your two main characters — the antagonist and the protagonist. Make these supporting characters memorable and quirky.

Don’t name characters if they don’t have a significant role to play in your book. Readers don’t need to know the names of the waitress, the jogger, the doorman, the receptionist, and the sales assistant.

3. Overwriting – Too Many Words

Everybody does it when describing a character’s thoughts, actions, or motivations. You know… the flowery prose, the reaching for descriptive heights, and the excessive internal monologue.

Good writing means writing clearly and economically. It means using at lost one of the five senses on every page. Use strong verbs, precise nouns, and proper sentence structure.

Great writing does not mean lots of words. It especially does not mean lots of big words. Don’t contrive a style. Correct words show everything.

4. Tormented Heroes – Too Many Thoughts, Not Enough Actions

‘One of the most-common mistakes that beginning writers make is leaving their characters alone. Writing, you may be alone. Reading, your audience may be alone. But your character should spend very, very little time alone. Because a solitary character starts thinking or worrying or wondering.’ ―Chuck Palahniuk

Most new writers spend too much time in their characters’ heads. To make characters human, we need to see them act.Ā Give them a story goal. Make them get up and do things. A character needs to move forward in a story. Readers need to see someone who is in trouble and understand why that matters.

A common mistake occurs when a character reviews an action, thinks it through, and then thinks about it again. This often reveals a lack of confidence on the writer’s part. They worry that they haven’t made their point clearly enough the first time.

Trust your reader. If you have shown us what happened, you don’t always need to explain it again. Your reader will lose interest if you keep repeating the same thoughts.

5. Lack of Setting – Where Am I?

Imagine watching a film where the characters live on a blank screen. That is the equivalent of having no setting in a novel.

Show your characters in their cars, homes, and offices. Use dƩcor, food, and medication to define them. To create a believable setting in a novel, characters must see, smell, hear, taste, and touch in that setting.

Characters can’t respond to their surroundings if they don’t have any. Make your characters uncomfortable. Put them in a crowded lift or a traffic jam. Make sure there is no coffee in the cupboard when they need it most.

Give your characters a life through setting.

The Last Word

We looked at five common mistakes beginner writers make when creating characters. We discussed cardboard cut-out characters, overcrowding, overwriting, too much thinking and not enough action, and the lack of setting. Remember to create real people, give them room to live, and let their surroundings help tell their stories.

Photo by Jacqueline MunguĆ­a on Unsplash


byĀ Amanda Patterson
Ā© Amanda Patterson

More posts from Amanda:
  1. What Does A Literary Agent Do?
  2. 7 Ways To Outline Your Memoir (& Why You Should Definitely Do It)
  3. Why You Need Strong Verbs When You Write
  4. 17 Ways To Make Your Novel More Satisfying
  5. How To Find Your Writing Voice
  6. The Ultimate Rewriting Checklist For Authors
  7. 14 Popular Crime Fiction Sub-Genres
  8. How To Use Secrets To Create Suspense
  9. 10 Ways To Start A Horror Story
  10. What’s The Difference Between A Commercial And A Literary Plot?

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

The post 5 Mistakes Beginner Writers Make When Creating Characters appeared first on Writers Write.

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

Disguising and Unmasking Antipattern

1 Share

    x = get_something()
    if x is none:
        return []
    elif isinstance(x, Item):
        return [x]
    elif isinstance(x, List):
        return x
    else:
        raise "Unknown type"

This made-up example is based on some actual agentically-written code.

The get_something() method returns Any, essentially (it’s not that obvious, but the type union might as well be Any).

There are a few things going wrong here - one being the sloppy return types, but the other being more crucial and appearing in many other forms in rather too much code in the wild.

The actual antipattern is this:

  • One method creates or discovers some knowledge.
  • The knowledge is not passed on to the caller or called methods
  • Other functions must unmask the data to determine its type/value, or in some cases must infer what was known in the other method.

In this case the answer was rather simple – the get_something (whose name was changed to protect the original context) is capable of producing a list of Items (also a changed name), so it should return a List[Item] clearly, since having zero items or one item is a degenerate case. No unmasking required!

If there is a reason the items cannot be collected (as opposed to searching and finding none) then the pythonic way of showing that is to raise an exceptoin. That is how python says ā€œI can’t do that.ā€

We see a lot of places where code is written with some unmasking of a disguised variable.


def record_entry(entry: Entry): None
   if isinstance(entry, EntryType1):
      record(entry.type_1_data)
   else if isinstance(entry, EntryType2):
      record(entry.type_2_data)
   else:
      raise ValueError(unhandledEntry)

This indicates that the Entry type is insufficiently abstract, since3 users receiving something of the Entry type must unmask the real type in order to access its data. O

I’ve also seen cases where a higher-level function retrieves some item from a database, and only passes the items’s ID to a lower-level fucntion, which must re-retrieve the item from the database by ID.

Also, I’ve seen one routine read a file and validate it, and then pass only the path to the next method (as a string), which is passed though the call tree, where the recipients have to re-validate that it’s a valid path and re-read the file.

More extreme cases have been seen where a calculation was performed and the results of the calculation written to a database. Later another process reads the data and tries to figure out how the value was calculated.

But it’s simpler this way!

This has been offered by humans and agents. It is nearly true. It does make the function itself easier - add a return anywhere you like, and declare that the function’s result isn’t typed (Any or a union of possible types).

The problem is that this makes the work less simple for all the callers. What isn’t carefully handled by the function itself must be done by each and every caller. That is duplication, and lowers the simplicity of each caller.

Do we want the work done by the one function, or by it’s dozen callers? Which is ā€œsimplerā€ really?

Taking your advantage at other people’s expense is essentially unethical, and in code it is not only rude, but it creates a pattern of spreading the ā€˜unmasking’ duties around. Remember, the LLMs will follow your patterns (and their own) so you don’t want them learning to code unethical functions and considering it normal.

Some Q-n-A

Is the typecast the problem?

No, that’s a symptom.

What’s wrong with writing defensive method code that chesk the params?

Nothing, when the caller is an unknown actor using unsanitized code

** But My method can return multiple things!

That MAY be a problem, but it may be the least-worse choice. Think hard on how you can avoid masking the type of the return and forcing all your callers to unmask them.

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

F# Weekly #46, 2026 – .NET 11 and fresh F#

1 Share

Welcome to F# Weekly,

A roundup of F# content from this past week:

News

Microsoft News

Videos

Blogs

Highlighted projects

New Releases

Rider 2026.3 is out, and it's šŸ”„šŸ”„ Day-one .NET 11 support, inc. C# 15 & Runtime Async debugging.šŸ”„ JetBrains Air: Run your coding agents in the IDE & review changes in one place.šŸ”„ New Home screen: Edit files, use terminal, or attach the debugger w/o opening a solutionTry it now: jb.gg/rd-263-bs

— JetBrains Rider (@rider.jetbrains.com) 2026-11-10T19:10:30.630Z

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

Buy Me A Coffee





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

Claim your Google Cloud Credits in Google AI Studio

1 Share
Claim your Google Cloud Credits in Google AI Studio
Read the whole story
alvinashcraft
18 hours ago
reply
Pennsylvania, USA
Share this story
Delete
Next Page of Stories