Friday, 7 August 2026

The Tiny Button With a Very Large Job

 THE SMALL-CHANGE FIELD GUIDE


Why the safest digital changes are often the ones that know how to do absolutely nothing until someone chooses otherwise.

 

A modest change, a visible escape route, and only a faint risk of alarming the upholstery.

Some of the most consequential changes on a website are small enough to fit inside a button. A new link, a different destination, a slightly altered sign-up path: none looks like an expedition at first glance. Yet each changes the route a real person takes, and real people have an unhelpful habit of using routes in ways that diagrams never quite predicted.

The useful lesson is not “avoid small changes.” That would leave us all staring solemnly at a perfectly preserved home page until the heat death of the universe. It is this: make small changes reversible by design.

A good default is a quiet safety device: the new behaviour appears only when its required information is present. Otherwise, the familiar path remains.

Start with the unchanged world

Before adding the new route, describe what happens when nobody has configured anything. This is the baseline—the ordinary, boring, dependable behaviour that should continue while the new option is still empty, unfinished, or being inspected by a cat.

That baseline matters because it turns a launch from a cliff edge into a switch. A blank destination, missing setting, or incomplete piece of content should not create a broken experience. It should preserve the existing one. In practical terms, the new behaviour becomes opt-in: no completed input, no changed journey.

Make activation deliberately obvious

When a change is ready, the act of turning it on should be simple and unmistakable. A clear label, one required value, and a visible save or publish step give the next person a fighting chance of understanding what they are doing.

Clarity is not just kindness to future colleagues. It is a form of reliability. Ambiguous controls invite accidental activation; explicit controls make the moment of change easy to review, test, and reverse.

Test the journey, not the control panel

It is tempting to stop when the setting looks correct. Settings are excellent at looking correct. They have had a great deal of practice.

Instead, follow the route a visitor will actually take. Open the page. Try the new action. Confirm that it reaches the intended destination. Then remove the activating information and confirm that the previous experience returns. This simple “on, then off” check catches a surprising number of mistakes: a saved-but-not-published edit, a misplaced link, a stale preview, or a fallback that was only theoretical.

Use a temporary destination when the real one is not ready

Sometimes the destination is still being prepared. That is not a reason to postpone all learning. Use a safe temporary destination to verify the mechanics, then clear it again. The key is to treat the test as a test: do not let a placeholder quietly become a public promise.

This approach separates two questions that are often accidentally welded together: “Does the mechanism work?” and “Is the final destination ready?” Once separated, both are much easier to answer honestly.

Leave a breadcrumb trail for the next human

A short guide should record three things: what activates the new behaviour, what happens when it is absent, and how to check the result. Those details turn a clever configuration into a reusable capability. They also reduce the odds that someone later will interpret an empty field as a mysterious failure rather than a purposeful safety net.

Small changes deserve this care precisely because they are easy to underestimate. The best ones feel uneventful to visitors: a choice works, a route leads where expected, and the old route remains available until the new one has earned its place.

Final takeaway: Design each small website change so it activates only with intentional input, preserves a dependable fallback, and can be verified by walking the same path your audience will take. That is not excessive ceremony. It is simply good engineering wearing sensible shoes.

Friday, 31 July 2026

The Door Is Not the Role

 



Most permission problems begin with an innocent idea: ‘We’ll give this kind of person that kind of access.’ It is tidy, fast, and has the satisfying clunk of a drawer closing. It also tends to fail the moment a real person has more than one responsibility, changes teams, covers a colleague, or simply behaves like a human being rather than a row in a spreadsheet.

A sturdier approach is to test access through journeys and capabilities. In plain English: start with what someone is trying to accomplish, then verify the smallest set of permissions that makes that task possible—no more, no less. This is not glamorous. Neither is a seatbelt. Both are rather more useful when the road gets interesting.

Start with a journey, not a label

A role is a shorthand. A journey is a story. ‘Administrator’ tells you almost nothing about the exact work being done; ‘review a request, correct a record, and hand it off’ tells you where to look for friction and risk.

For each important journey, write a small scenario with a beginning, middle, and end. Keep it concrete: what is the person trying to achieve, what information should they see, what action should they be able to take, and what must remain out of reach? The final question is not pessimism. It is just how the universe keeps its filing cabinet from eating the cat.

Build a capability map

Instead of beginning with a long list of job titles, name the capabilities your product actually needs. Common examples include viewing a record, editing a record, approving a change, inviting another person, exporting data, or changing billing details. A person may need several of these; a role may bundle some of them; but the capability remains the useful unit of truth.

  • Name actions in verbs: view, create, edit, approve, invite, export, manage.
  • Separate access to see something from access to change it.
  • Treat high-impact actions—money movement, deletion, publishing, user management—as their own capabilities.
  • Make temporary or exceptional access visible and easy to remove.

Test the edges where bugs prefer to live

The happy path is necessary, but it is also where systems are most charmingly well behaved. The value is in testing the boundary conditions: a person who can view but not edit; someone whose access changes mid-journey; a delegated helper; a person with two overlapping responsibilities; an account that should lose a capability immediately.

A compact test matrix can keep this practical. For every journey, include at least one expected success and one expected refusal. If a refusal is vague, confusing, or quietly bypassable, it is telling you something useful about the design—not merely about the test.

Make refusals useful

A blocked action is not automatically a failure. Sometimes it is the system doing its job with admirable restraint. The experience matters, though. A useful refusal says what happened in ordinary language, avoids revealing sensitive information, and gives the person a sensible next step. ‘You cannot do that’ is technically accurate in the way a locked shed is technically architecture.

Where possible, show people the boundary before they collide with it. Hide or disable actions they cannot use, explain why only when it is safe to do so, and offer a route to the right owner or process. Clarity reduces support requests; mystery merely relocates them.

Keep the test set alive

Permission models drift as products grow. New features add new actions, teams change shape, and one ‘small exception’ wanders off to breed in the woods. Turn the journeys that matter most into repeatable checks. Revisit them when capabilities change, not only after an incident has already delivered its tiny brass band.

The goal is not a perfectly clever access model. It is a comprehensible one: people can do the work they are meant to do, they cannot quietly do the work they are not, and everyone can explain why. That is a rare form of calm in software—and worth cultivating.

Final takeaway

Test permissions through the real journeys people take and the capabilities they need. Roles are useful labels; journeys reveal whether the doors actually lead anywhere.

Friday, 24 February 2023

SlackAPIPostOperator unable to send to private slack channel.

 
I am using SlackAPIPostOperator from an Airflow Dag.  And have successfully made a bot that will send messages to my Slack Channel 

slack_post = SlackAPIPostOperator(
task_id="slack_notification",
channel="#my_slack_channel",
username="airflow",
text="A test message from Airflow. Please ignore",
token="#######",
dag=dag,
)


However, when I use the same logic on a Private channel it wouldn't work.  And I couldn't see my bot to be able to 'invite' it to the channel.  


To fix this ...

To grant your bot permission to join private channels, you will need to ensure that it has the channels:join and channels:read OAuth scopes. Here’s how you can check and modify your bot’s scopes:
  1. Go to the Slack API website and navigate to the “OAuth & Permissions” section of your app’s settings.
  2. Scroll down to the “Scopes” section and check that your bot has the channels:join and channels:read scopes.
  3. If the scopes are not already added, click on the “Add an OAuth Scope” button and select the scopes from the dropdown list.
  4. After adding the scopes, make sure to re-install your app in the workspace to apply the changes.
After , that I had to re-invite the Bot to the Private Channel  ( through the add people ) and it worked.

Wednesday, 4 January 2023

In Wagtail GeoLocation Widget how to set a Default location ( and set zoom )

 To set the default map location in the wagtailgeowidget package, you can define the GEO_WIDGET_DEFAULT_LOCATION setting in your Django settings file.

Here is an example of how to do this:

GEO_WIDGET_DEFAULT_LOCATION = {"lat": 50.5230, "lng": -4.6558}

This will set the default map location to the coordinates 50.5230° N, 4.6558° W (Bodmin, Cornwall, United Kingdom).

The GEO_WIDGET_DEFAULT_LOCATION setting should be a dictionary with keys 'lat' and 'lng' representing the latitude and longitude, respectively. The values should be float values.

You can also set the default map location dynamically based on the value of a field in your model by defining a callable as the value of the GEO_WIDGET_DEFAULT_LOCATION setting.

Here's an example of how to do this:

from django.conf import settings def get_default_location(): # Code to retrieve default location from model goes here return {"lat": 50.5230, "lng": -4.6558} GEO_WIDGET_DEFAULT_LOCATION = get_default_location

This will set the default map location to the value returned by the get_default_location function.

Tuesday, 31 May 2022

Django - Grabbing a JSON object from QueryDict

 
What I'm doing here is passing some JSON through the 'rest.framework' .  The problem is that when using 'request.json'  I'm seeing the following being returned. 

<QueryDict: {'': ['{"time":{"0":"06:00","1":"07:00","2":"08:00","3":"09:00","4":"10...


What I need to do is extract the JSON out .  To do this , the following works. 

list(data.values())[0]




Wednesday, 25 May 2022

M1 Chip issue with AWS Lambda SAM Layers

 

Just a quick note , if you've searched and found this blog then I expect you already know deep down that the M1 chip is your issue.  There is a solution though.  Let me explain. 


So, what we've been doing is using Lambda Layers to install Python packages on our Lambda installation.  However, when putting the same code on my Mac ( M1 Chip ) it doesn't work ! 

https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html

It does work if we take the zip file from the AMD64 chipped Mac , and put that on my Mac and proceed. 

That is the solution we're having to use at the moment , the zip needs to be compiled on the AMD64 chip , doing it on the M1 chip won't work. 


Tuesday, 17 May 2022

Could not 'pipenv install email' - 'python setup.py egg_info did not run successfully.' error.

Here's the error code I was getting. 

Installing email... Error: An error occurred while installing email! Error text: Collecting email Using cached email-4.0.2.tar.gz (1.2 MB) Preparing metadata (setup.py): started Preparing metadata (setup.py): finished with status 'error' error: subprocess-exited-with-error × python setup.py egg_info did not run successfully. │ exit code: 1 ╰─> [1 lines of output] ERROR: Can not execute `setup.py` since setuptools is not available in the build environment. [end of output] note: This error originates from a subprocess, and is likely not a problem with pip. error: metadata-generation-failed × Encountered error while generating package metadata. ╰─> See above for output. note: This is an issue with the package mentioned above, not pip. hint: See above for details. This is likely caused by a bug in email. Report this to its maintainers. ✘ Installation Failed



The recommended solution across the web seemed to be to run the following. 

pipenv install --upgrade setuptools


However, this didn't work for me.  I needed to use the '--pre' tag. 

pipenv install --pre email


This now works, the version I had the issue with was 'email-4.0.2'


So, if you have a 'email-4.0.2 won't pipenv install' issue then I hope this post helps.