Doing It Wrong

learn in public

Site Navigation

  • Home
  • Books
  • Work & Play

Site Search

You are here: Home / Archives for 2024

Archives for 2024

The Pragmatic Programmer: Chapter 8

posted on January 29, 2024

Before the Project

This chapter will focus on project management from an agile perspective (framework-free).

The Requirements Pit

Requirements gathering is more like requirements digging—nobody knows exactly what the want. Programmers help people understand what they want. The requirement (once expressed) should be seen an invitation to explore.

For example, the statement “Shipping should be free on all orders over $50.” should beget a bevy of questions about including tax, which shipping methods are included, internationalization, frequency of changing that $50 number, etc. The client (who has great domain knowledge, hopefully) has probably thought of some of these cases, but discussing them helps flesh out the actual requirements. A developer’s role is to interpret what the client says and feed back to them the implications.

Requirements are a process. They are learned in a feedback loop. Help your client understand the consequences of their stated requirements. Sometimes you won’t know the domain of knowledge well enough to provide salient feedback, so use “is this what you meant?” through mockups and feedback.

Walk in your client’s shoes. Work with a user to think like a user.

There’s a difference between requirements and policy. Policy should be metadata. Policy can be something like “Only an employee’s supervisor and the personnel department may view that employee’s record.” Taken literally as a requirement, it might be hard-coded. Taken as policy, you’d build / use an access control system that allowed authorized users to view a record—defining what is an authorized user would then be metadata (or configuration).

There’s also a difference between doing what the requirement asks, but not how the customer wants. Early prototypes and tracer code are key to nailing both.

Requirements are best recorded as user stories—describing a small portion of what the application does from the perspective of the user. Keep the stories small enough to fit on an index card (real or virtual), which encourages develpoers to ask clarifying questions (record the answers, for documentation).

Developers should maintain a project glossary that contains all domain-specific vocabulary for that project. It’s hard to succeed on a project if users and developers cal the same thing by different names.

Section Challenges

  • [ ] Exercise 33 Which of the following are probably genuine requirements? Restate those that are not to make them more useful (if possible).
  1. The response time must be less than ~500ms.
  2. Modal windows will have a gray background.
  3. The application will be organized as a number of front-end processes and a back-end server.
  4. If a user enters non-numeric characters in a numeric field, the system will flash the field background and not accept them.
  5. The code and data for this embedded application must fit within 32Mb.
  • [ ] Can you use the software you are writing? Is it possible to have a good feel for requirements without being able to use the software yourself?
  • [ ] Pick a non-computer-related problem you currently need to solve. Generate requirements for a non-computer solution.

Solving Impossible Puzzles

The key to solving puzzles is both to recognize the constraints placed on you and to recognize the degrees of freedom you do have, for in those you’ll find your solution. Challenge any preconceived notions and evaluate whether or not they are real, hard-and-fast constraints. Categorize and prioritize your constraints.

You can help yourself solve puzzles by getting out of your own way. If you get stuck, do something else so that you let your subconscious (an amazing associative neural net) can process it. If you can’t do that, try to explain the problem to someone. And, like Hitchiker’s Guide to the Galaxy says, “DON’T PANIC”.

Section Challenges

  • [ ] Take a hard look at whatever difficult problem you are embroiled in today. Can you cut the Gordian knot? Do you have to do it this way? Do you have to do it at all?
  • [ ] Were you handed a set of constraints when you signed on to your current project? Are they all still applicable, and is the interpretation of them still valid?

Working Together

I’m a lover of pair programming, so this section really resonated. I love the resultant product that comes from the tensions of two people (or more) working out a solution together. I think two is almost always better than one.

  • [ ] Have you tried pair programming? What was your experience?

The Essence of Agility

From the manifesto:

  • Individuals and interactions over processes and tools
  • Working software over comprehensive documentation
  • Customer collaboration over contract negotiation
  • Responding to change over following a plan

Any framework or method that pushes the right side over the left side should be abandoned. Agility is:

  1. Work out where you are.
  2. Make the smallest meaningful step towards where you want to be.
  3. Evaluate where you end up, and fix anything you broke.

Section Challenges

  • [ ] The simple feedback loop isn’t just for software. Think of other decisions you’ve made recently. Could any of them have been improved by thinking about how you might be able to undo them if things didn’t take you in the direction you were going? Can you think of ways you can improve what you do by gathering and acting on feedback?

Filed Under: Development Tagged With: Book, Notes

The Pragmatic Programmer: Chapter 7

posted on January 10, 2024

While You Are Coding

Coding is an intensely creative act that happens through instincts / nonconscious thoughts and the active thoughtful application of principles.

Listen to Your Lizard Brain

The first trick is to notice that it’s happening; then work out why. You may not be able to, but try to crystallize it into something solid that you can address. Let your instincts contribute to your performance.

  • fear of the blank page is usually caused by
  • some kind of important doubt lurking below perception
  • you might be afraid you’ll make a mistake
  • how to figure out what’s lurking below perception?
  • let your subconscious brain process the problem by doing something else away from the keyboard
  • try externalizing the issue: rubber duck, doodle
  • maybe even prototype around the problem space giving you disease; this context switch can help you work through it
  • in other people’s code, notice what give you pause so that you can lean from the person who wrote it

Filed Under: Development Tagged With: Book, Notes

Form-Associated Custom Elements (FACE)

posted on January 8, 2024

I was using the static property formAssociated on some Lit -based web components I’m writing for a data-subsetting form at work. I’m bummed that it doesn’t click well with my brain. I get it, though—APIs are hard to design, and I can’t even imagine the level of skill needed to build the web component spec. I just wish it had been an attribute on a form input, not a static property on the web component class.

This is how you define a form-associated web component that can set its value on the parent form, using Lit element:

class SubsetterPart extends LitElement {
  static formAssociated = true; // (1)

  /*
    Look, you do your private class fields however you want
    when you're at home. In this house we use proper native
    private class fields.
  */
  #internals;

  #name = 'subsetter-part'

  constructor() {
    super();

    this.#internals = this.attachInternals(); // (2)
  }

  connectedCallback() {
    super.connectedCallback();

    this.setAttribute('name', this.#name) // (3)
  }

  @state();
  state = {};

  #handleChange(event) {
    const { value } = event.target

    this.#internals.setFormValue(value) // (4)

    /* set some state and stuff... */
  }

  /* The rest of your class stuff...including the form <input>s */
Code language: TypeScript (typescript)

This associates your custom element with a parent form (1), lets you access some shared parent state (2), and adds a name onto the host element (shadow host? whatever…the web component, itself) so that the parent form’s submit handler can read it from the FormData (3), then set’s that value through a method available on the shared state (4). A quick aside: learning enough about FACE web components to be able to type that sentence cost me three days (they felt so unproductive).

But why? In this <subsetter-part> custom element, why would I want the host to control whether it associates itself with a form? Well, that might be useful if you were making a web component that wanted to itself be thin wrapper around a form control—think maybe a custom temporal picker element that might be used to choose a temporal point [start] for subsetting data.

  /* The rest of your class stuff...including the form <input>s. */

  #validity = {
   /* Some instance state about what's a valid start date, end date. */
  }

  render() {
    return html`
      <label>
        <slot name="label">choose a date</slot>
        <input
          .value=${this.state.date}
          @change=${this.#handleChange}
          name="date"
          type="date"
          min=${this.validity.fromDate}
          max=${this.validity.toDate}
        >
      </label>
    `
  }
Code language: TypeScript (typescript)

If you’re vision of web components includes that use case, I’m happy. We share that vision. But if your vision of web components ends at that, I’m bummed.

What if I want to build a form control that is itself made up of a few form controls? Like what if I wanted to make a custom element that allowed me to select a temporal range [start, end] for that same data subsetting task?

I’m going to code this and you’re maybe going to have the objection that I could have built this with a series of the same “thin wrapper around a form control” components that I just wrote plus a container component, and you’re right. It’s not that I can’t get this done with the current API, it’s that it doesn’t click with my brain. Anyway, a few form controls like this:

  /* The rest of your class stuff...including the form <input>s. */

  #validity = {
   /* Some instance state about what's a valid start date, end date. */
  }

  render() {
    return html`
      <label>
        <slot name="start-label">choose a start date</slot>
        <input
          .value=${this.state.startDate}
          @change=${this.#handleChange}
          type="start-date"
          name="start-date"
          min=${this.validity.fromDate}
          max=${this.validity.toDate}
        >
      </label>

      <label>
        <slot name="end-label">choose an end date</slot>
        <input
          .value=${this.state.endDate}
          @change=${this.#handleChange}
          type="end-date"
          name="end-date"
          min=${this.validity.fromDate}
          max=${this.validity.toDate}
        >
      </label>
    `
  }
Code language: TypeScript (typescript)

So, altogether like this:

class SubsetterPart extends LitElement {
  static formAssociated = true; // (1)

  #internals;

  #name = 'subsetter-part'

  constructor() {
    super();

    this.#internals = this.attachInternals(); // (2)
  }

  connectedCallback() {
    super.connectedCallback();

    this.setAttribute('name', this.#name) // (3)
  }

  @state();
  state = {};

  #handleChange(event) {
    const { value } = event.target

    this.#internals.setFormValue(value) // (4)

    /* set some state and stuff... */
  }

  #validity = {
   /* Some instance state about what's a valid start date, end date. */
  }

  render() {
    return html`
      <label>
        <slot name="start-label">choose a start date</slot>
        <input
          .value=${this.state.startDate}
          @change=${this.#handleChange}
          type="start-date"
          name="start-date"
          min=${this.validity.fromDate}
          max=${this.validity.toDate}
        >
      </label>

      <label>
        <slot name="end-label">choose an end date</slot>
        <input
          .value=${this.state.endDate}
          @change=${this.#handleChange}
          type="end-date"
          name="end-date"
          min=${this.validity.fromDate}
          max=${this.validity.toDate}
        >
      </label>
    `
  }
Code language: TypeScript (typescript)

Whereas if formAssociated had been an attribute, this is how I’d imagine it being used:

class SubsetterPart extends LitElement {
  @state();
  state = {};

  #validity = {
   /* Some instance state about what's a valid start date, end date. */
  }

  #handleChange = (event) => {
    /* Some of the most amazing change-handling you've seen, admit it. */
  }

  render() {
    return html`
      <label>
        <slot name="start-label">choose a start date</slot>
        <input
          .value=${this.state.startDate}
          @change=${this.#handleChange}
          type="start-date"
          name="start-date"
          min=${this.validity.fromDate}
          max=${this.validity.toDate}
          formassociated
        >
      </label>

      <label>
        <slot name="end-label">choose an end date</slot>
        <input
          .value=${this.state.endDate}
          @change=${this.#handleChange}
          type="end-date"
          name="end-date"
          min=${this.validity.fromDate}
          max=${this.validity.toDate}
          formassociated
        >
      </label>
    `
  }
Code language: TypeScript (typescript)

In my magical greenfield world where such a thing is possible, the formassociated attribute would tell the parent form that this form control and some key associations should pierce through the Shadow DOM to a parent form. Maybe formassociated="some-form-id" could even be a thing, like form on a button, etc. Multiple levels of form -> Shadow DOM could have some common-sense use case like using the first available (but form attribute is a nice escape hatch). Because formassociated is on the input, any form-control-associated label can come along for the ride, too!

In the end, and like I was starting to say previously, I know this same thing could be build with the current API: I just need to chunk components differently. But that’s what I dislike! Sometimes a couple of form controls is just the right amount of atomicity. Sometimes I want to share that state and functionality in one class / custom element instead of two. And making everything a well-abstracted component takes time and boilerplate and more files. The hypothetical attribute formassociated lets me chunk my state / control my atomicity way better than the static property formAssociated.

Filed Under: Development Tagged With: Web Components

  • « Previous Page
  • 1
  • 2
  • 3

Profile Links

  • GitHub
  • Buy Me a Coffee?

Recent Posts

  • Event Listeners
  • A Philosophy of Software Design
  • The Programmer’s Brain
  • Thoughts on Microservices
  • API Design Patterns

Recent Comments

No comments to show.

Archives

  • May 2025
  • September 2024
  • July 2024
  • March 2024
  • February 2024
  • January 2024
  • December 2023
  • November 2023
  • October 2023
  • December 2022
  • December 2021

Categories

  • Development