Simon blogs

Started by Simon, October 18, 2015, 06:05:44 AM

Previous topic - Next topic

0 Members and 4 Guests are viewing this topic.

Simon

#375
No hidden hotkeys.
  • Every function gets a GUI button with the hotkey printed on it.
  • If it can't be a GUI button, e.g., directional select or priority invert, the function gets a tooltip as soon as it's useful.
  • Can you automate the function to reduce mental burden?
  • Esc is special. It's a common hidden key in games, and it's like the home button on smartphones.
Still, in singleplayer Lix, you don't even need Esc; you can nuke. The nuke has a GUI button with a printed hotkey.

No dialogs.
  • If you must display a message, display it without interrupting the control flow.
  • The classic essay on this rule is Death to the Dialog Box on Coding Horror, 2004.
  • Example: Level loading errors show in the Lix level preview screen. You can keep clicking levels.
  • Five years ago, I followed this to an extreme, removed the end-of-singleplayer dialog, and printed game outcomes into the level browser. Only when people wanted a next-level button, the browser would have become too crowded.
  • Dialogs break flow the most after you perform action/command X and usually get no dialog after X.
On hard-to-remove dialogs:
  • Menus (that we implement as dialogs) are okay when we consciously summon them for their unique content, e.g., the options menu.
  • The menu, if it is a modal dialog (i.e., you must finish dealing with it before you may continue your normal work), must still pull its weight. If we only have 1-2 small options, can we stick those into another existing screen?
  • I believe that dialogs are less annoying (but still annoying) when you expect a drastic context switch anyway and have no clear next goal.
Example: You return from game to the menu. I still want to avoid dialogs here, but they're less nasty here than elsewhere.

Example: You start a time-consuming computation with nontrivial results that you rarely need, e.g., mass replay verification from inside the GUI.

Counterexample: When you need the replay verification often, dialogs will get annoying. E.g., you maintain Lix and want to test all packs before each release. Run the replay verification from the command line:
$ lix --coverage replays/path/to/your/pack

Counterexample: After you save a level in the editor, you usually want to playtest it. This is a clear goal; you don't want strange dialogs to get in the way despite the drastic context switch between editing and playing. Thinking about this, this leads us to more design insight: The context switch between editing and playing a level shouldn't be drastic in the first place. You're working on a single level throughout, after all. IIRC, back in the day, GigaLem suggested a button in the editor to playtest immediately.

-- Simon

Simon

#376
Cultural Moss

  • namida supports different spritesheets.
  • WillLem draws the lemminas.
  • namida adds the laserer.

Who is responsible to draw the lasering lemmina? Certainly WillLem.

  • Simon writes the game physics.
  • geoo builds a level.
  • Simon merges the level into the main release.
  • Simon changes the physics, breaking the level's solution.

Is geoo still responsible to fix his level?

Or is Simon responsible to fix the level because Simon manages the level's releases? What happens if Simon doesn't know the solution and geoo is away on a month-long trip without internet? Should Simon watch geoo's intended solution from the replay collection and spoil the puzzle for himself?



  • DMA covers the blocks in Steel Works with moss.
  • namida and Simon conspire independently make the moss destructible.
  • DMA chooses not to register on Lemmings Forums to submit an updated level.

Who is responsible to fix Steel Works? What is even the best fix? Should the fix be in the game physics or in the level's moss placement? Oh, the popcorn!

  • You're a compiler writer.
  • You recognize these problems as real-life variants of the Expression Problem.
  • If you have an open class hierarchy and the codebase has subclasses everywhere, it's hard to add new pure virtual methods to the base.
  • If you have a closed class hierarchy with a visitor pattern, you can define visitors everywhere in the codebase, but no new subclasses.

This one is easy. You are responsible. If you run into the problem of adding new expressions often, your language is still young, and you're likely the only compiler writer. Thus, you are responsible to fix the hard-to-fix half of the problem.

  • You're a language designer for a popular language.
  • Other people write software in your language.
  • You want to change the syntax from "int foo()" to "auto foo() -> int".

This is also easy. You must maintain both styles to the end of ages. You push the burden of choice down to developer teams; they can pick a style for the codebase or accept a mix.



The hard variant of this problem is: You're designing an uncommon language and want to change the syntax. In the early days of Zig, you had to write "fn foo() -> i32" to declare a function that returns a signed 32-bit integer. The designers thought that "fn foo() i32" is better. Their Reasoning: foo is the function that maps into the type i32 and foo(), i.e., the call of the function, produces a single i32, but is not itself a mapping of anything into the type i32. Nowadays, it's a hard error to write "fn foo() -> i32". The Zig compiler tells you that it expects a return type, not "->". Thus, early during Zig development, users had take off every zig arrow in all (= the few) of their codebases.

How to handle cultural moss?

Have as few problems of this kind as possible. If you have an ecosystem, you'll probably have at least one such problem, otherwise people can't contribute creatively. Decide if you even want to build an ecosystem in the first place. Maybe your players can only solve levels, not build any? Maybe you don't need a core component (e.g., a computer game) that needs maintenance in the first place?

Find the axis that grows more often (levels) and make it easy to add to this axis. Accept that even small breaking changes the other axis (physics) will be hard.

If you have more than one such problem, avoid overlap. You already have two axes (levels and physics) whose span is hard to fill. You don't want a third axis. E.g., namida absolutely forbids to change existing tiles. In Lix, I allow tile changes in the centrally released tilesets, but I treat them like physics changes, thereby merging two axes (tiles and physics) into one.

Fail noisily. Cover your levels with solution replays. Test the replays after every change to either axis. In software, even though the visitor pattern in classical OOP languages brings boilerplate, it breaks clearly at compile time when a case is missing.

-- Simon

Simon

#377
Bug or Feature?

It's a bug if:

  • The program does X.
  • The programmer implemened X.
  • The programmer wanted Y, not X.
  • The user wants Y, not X.

It's a feature request if:

  • The program does X.
  • The programmer implemented X.
  • The programmer wanted X.
  • The user wants Y, not X.

The only difference is in line #3. When you force your users to classify requests as bug or as feature wish, you're forcing your users to read your mind. Yes, the user can eventually guess what you wanted if he's been around in the culture for a while. But why force him?

If we remove the need for the user to classify, we get:

  • The program does X.
  • The user wants Y, not X.

Indeed, feature requests are, at heart, bug reports. The program doesn't already do what the user expected. You can change the program or reject the request, regardless classification into bug report or feature request. Prioritize the requests by urgency, e.g., by average impact. A missing feature may well be annoying and urgent -- and I'll call that a bug with a straight face.

Another fun case:

  • The program does X.
  • The user wanted Y, not X, but has triggered X.
  • The programmer got the implementation of X right, according to the design of X.
  • The programmer disagrees with the user that X or Y or the program as a whole is buggy.

This is often a genuine bug, too: The bug is in the UI, or in the higher-level design of the program.

There is a grey area here. It's one thing if two buttons are too close to each other, enticing mouse slips. (Edit: Following example is bad; see next 2 posts for clarification.) Its another if you must, to delete the database, click Delete Database, type YES, DELETE DATABASE into the big red box and click and hold OK for 5 seconds. You can mouse-slip onto the Delete button, your cat might then run across the keyboard and accidentally push caps-lock and enter those 20 tokens, ...

-- Simon

Dominator_101

I'm not sure if I totally agree with your classification, at least for all cases. When talking about a feature request, in most cases to me that means something the program doesn't attempt to do at all, so it doesn't make sense to think of it in terms of 'programmer implemented X, user wants Y.' For example, if I'm making a text editor, it doesn't really make sense to be like 'The editor allows copy/pasting, but the user wants it to do spellcheck,' these are mutually exclusive things. I guess you could like, think of it as 'The program does (not spellcheck),' 'The programmer implemented (not spellcheck),' 'The user wants (spellcheck), not (not spellcheck)' but that seems...a bit clunky?

To me, I'd say the main breakdown is like this:
It's a bug if:
  • The program attempts to do X
  • The program actually does Y

It's a feature request if:
  • The program does not attempt to do X
  • I'd be good if it did X

Now, I say the main breakdown because I think there is a middle ground that does act more like your example; existing things in the program that could act multiple ways without necessarily being wrong depending on your point of view. Just as a simple example, whether a time field defaults to the user's timezone or GMT. Depending on the context, there could be valid cases for both. Things like these I don't think I'd particularly care whether it's classified as a bug or a feature, especially if an end user is the one entering the report. Maybe these are the types of cases you are referring to, based on your flow.

I think agree with the general assessment of UX bugs, they will typically be things that act more like the later case of 'not technically wrong but could be better.' There's only so much you can add to protect the extreme edge-case user without becoming overly cumbersome for the general use (sure, multiple more confirmations on delete make it safer, but is it worth making 99.9% of cases that much worse to account for the .1% that may or may not happen).

Simon

#379
Quote from: Dominator_101 on January 07, 2025, 06:54:01 PMThe program does (not spellcheck),' 'The programmer implemented (not spellcheck),' 'The user wants (spellcheck), not (not spellcheck)' but that seems...a bit clunky?

Correct, that's exactly what I have in mind.

The nuance is: I don't want to inflict the choice between bug or feature on the user. If you're the designer or implementor, and such a classification helps you, by all means, classify. I've noticed that it hasn't helped me much.

Lix miners throw the pickaxe, but bashers/diggers won't throw the shovel/jackhammer. I call this a bug. Is this a missing feature or a bug in your view? Is it both?

In the graphics files, all three flying tools have sprites. In the physics, all three skills notify the effect sink (= the eye candy subsystem) to throw the tool. The difference is: The effect sink's method for pickaxe adds an animated pickaxe and rolls for initial speed, but the effect sinks's two methods for shovels and for jackhammers are still empty. Do you need the information in this paragraph to decide between bug/feature/both?

You can argue that the program does something very similar already, and therefore "attempts to do X" for X = throwing the ground-removing tool. But we can imagine requests à la: Please add a flying backpack when a builder has run out of bricks.

I'd even call it a bug report if a deranged user complained that Lix cannot understand chess notation. I'd reject this bug report as out of scope.

Quote from: Dominator_101 on January 07, 2025, 06:54:01 PMprotect the extreme edge-case user without becoming overly cumbersome for the general use

Right, irrevocable commands aren't the best example for a source of UX bugs that you and I, but not everybody else, would classify as UX bug. They entice those cumbersome safety measures that get in the way, and it's hard to say how much safety is best.

It's better to make the command easy-to-access and revocable, e.g., offer undo. This is hard in the physical world, and it's hard when files are involved, e.g., other processes may react to the database deletion.

A better example is a misleadingly named button/command/..., where the user doesn't classify his mistake as a typo or a mouse slip. E.g., there is a nontrivial difference between these two:

git rebase A B
git rebase --onto A B

-- Simon

Dominator_101

The basher/digger example is something I would put in the same classification as the timezone case I mentioned where I wouldn't really care about the classification, especially if it was an end user entering it. Sure, I might lean one way or the other if I'm entering it but if I'm having an end user enter it shouldn't really matter for something along those lines.

Honestly though if I have some sort of form for the end user to enter stuff I probably wouldn't even worry about classification at that point; I'd probably just have it be something like 'Enter a ticket' and not ask them/call it either one. No reason to give them the illusion of choice if there's a 50% chance they'd be wrong either way. In theory I'd be reviewing them afterwards anyway and can classify them at that point by adding a tag or whatnot if it helps the organization.

All this said, usually in my day-to-day work it doesn't particularly matter to me what it's considered, I just work on the tickets on the board. The classifications are probably used by the product owners for prioritization/etc but doesn't matter much for the actual dev work. And I haven't had any personal projects complicated enough to worry about ticketing systems.