Hacker News new | ask | show | jobs
by skydhash 205 days ago
> and you know exactly what I'm describing.

I won't unless both you and I have a shared context which will tie each of these concept to a specific thing. You said "async function", and there's a lot of languages that don't have that concept. And what about the permissions of the s3 bucket, what's the initial time of the wait time? And what algorithm for the resizing? What if someone sent us a very big image (let say the maximum that the standard allows).

These are still logic questions that have not been addressed.

The thing is that general programming languages are general. We do have constructs like procedure/functions and class, that allows us for a more specialized notation, but that's a skill to acquire (like writing clear and informative text).

So in pseudo lisp, the code would be like

   (defun fn (bytes)
     (when-let\* ((png (byte2png bytes))
                 (valid (and (valid-png-p png)
                             (square-res-p png)))
                 (small-png (resize-image png))
                 (bucket (get-env "IMAGE_BUCKET"))
                 (filename (uuid)))
       (do-retry :backoff 'exp
                 (s3-upload bucket small-png))))
And in pseudo prolog

  square(P) :- width(P, W), height(P, H), W is H.
  validpng(P, X) :-  a whole list of clauses that parses X and build up P, square(P).
  resizepng(P) :- bigger(100,100, P), scale(100, 100, P).
  smallpng(P, X) :- validpng(P, X), resizepng(P).
  s3upload(P): env("IMAGE_BUCKET", B), s3_put(P, B, (exp_backoff(100))))
  fn(X) :-  smallpng(P, X), s3upload(P)
So what you've left is all the details. It's great if someone already have an library that already does the thing, and the functions has the same signature, but more often than not, there isn't something like that.

Code can be as highlevel as you want and very close to natural language. Where people spend time is the implementation of the lower level and dealing with all the failure modes.

1 comments

Details like the language/stack and S3 configuration would presumably be somewhere else in the spec, not in the description of that particular function.

The fact that you're able to confidently take what I wrote and stretch it into pseudocode with zero deviation from my intended meaning proves my point.

To draft a spec like this, it would take more time and the same or more knowledge than to just write the code. And you still won’t have reliable results, without doing another lengthy pass to correct the generated code.

I can create a pseudocode because I know the relevant paradigm as well as how to design software. There’s no way you can have a novice draft pseudo-code like this because they can’t abstract well and discern intent behind abstractions.

I don't agree that it would take more time. Drafting detailed requirements like that to feed into coding agents is a big part of how I work nowadays, and the difference is night and day. I certainly didn't spend as much time typing that function description as I would have spent writing a functional version of it in any given language.

Collaborating with AI also speeds this up a lot. For example, it's much faster to have the AI write a code snippet involving a dependency/API and manually verify the code's correctness for inclusion in the spec than it is to read though documentation and write the same code by hand.

The feat of implementing that function based on my description is well within the capabilities of AI. Grok did it in under 30 seconds, and I don't see any obvious mistakes at first glance: https://grok.com/share/c2hhcmQtMw_fa68bae1-3436-404b-bf9e-09....

I don't have access to the grok sample you've shared (service not available in my region)

Reading the documentation is mostly for gotchas and understanding the subsystem you're going to incorporate in your software. You can not design something that will use GTK or sndio without understanding the core concepts of those technologies. And if you know the concepts, then I will say it's easier and faster to write the code than to write such specs.

As for finding samples, it's easy on the web. Especially with GitHub search. But these days, I often take a look at the source code of the library itself, because I often got questions that the documentation don't have the answer for. It's not about what the code I wrote may do (which is trivial to know) but what it cannot do at all.

Ah, weird, that's good to know. Well here's the code:

    import { env } from './env';
    import { v4 as uuidv4 } from 'uuid';
    import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
    import sharp from 'sharp';

    async function retry<T>(fn: () => Promise<T>, maxAttempts: number): Promise<T> {
      let attempt = 1;
      while (true) {
        try {
          return await fn();
        } catch (error) {
          if (attempt >= maxAttempts) {
            throw error;
          }
          const delayMs = Math.pow(2, attempt - 1) * 100;
          await new Promise((resolve) => setTimeout(resolve, delayMs));
          attempt++;
        }
      }
    }

    export async function processAndUploadImage(s3: S3Client, imageData: Uint8Array): Promise<string> {
      let metadata;
      try {
        metadata = await sharp(imageData).metadata();
      } catch {
        throw new Error('Invalid image');
      }

      if (metadata.format !== 'png') {
        throw new Error('Not a PNG image');
      }

      if (!metadata.width || !metadata.height || metadata.width !== metadata.height || metadata.width < 100) {
        throw new Error('Image must have a 1:1 aspect ratio and resolution >= 100x100');
      }

      const resizedBuffer = await sharp(imageData).resize(100, 100).toBuffer();

      const key = `${uuidv4()}.png`;

      const command = new PutObjectCommand({
        Bucket: env.IMAGE_BUCKET,
        Key: key,
        Body: resizedBuffer,
        ContentType: 'image/png',
      });

      await retry(async () => {
        await s3.send(command);
      }, 100);

      return key;
    }
The prompting was the same as above, with the stipulations that it use TypeScript, import `env` from `./env`, and take the S3 client as the first function argument.

You still need reference information of some sort in order to use any API for the first time. Knowing common Node.js AWS SDK functions offhand might not be unusual, but that's just one example. I often review source code of libraries before using them as well, which isn't in any way contradictory with involving AI in the development process.

From my perspective, using AI is just like having a bunch of interns on speed at my beck and call 24/7 who don't mind being micromanaged. Maybe I'd prefer the end result of building the thing 100% solo if I had an infinite amount of time to do so, but given that time is scarce, vastly expanding the resources available to me in exchange for relinquishing some control over low-priority details is a fair trade. I'd rather deliver a better product with some quirks under the hood than let my (fast, but still human) coding speed be the bottleneck on what gets built. The AI may not write every last detail exactly the way I would, but neither do other humans.

As I’m saying, for pure samples and pseudo code demo, it can be fast enough. But why bring in the whole s3 library if you’re going to use one single endpoint? I’ve checked npmjs and sharp is still in beta mode (if they’re using semver). Also, the code is parsing the imagedata twice.

I’m not saying that I write flawless code, but I’m more for less feature and better code. I’ve battled code where people would add big libraries just to not write ten lines of code. And then can’t reason when a snippet fails because it’s unreliable code into unreliable code. And then after a few months, you got zombie code in the project. And the same thing implemented multiple times in a slightly different way each time. These are pitfalls that occur when you don’t have an holistic view of the project.

I’ve never found coding speed to be an issue. The only time when my coding is slow is when I’m rewriting some legacy code and pausing every two lines to decipher the intent with no documentation.

But I do use advanced editing tools. Coding speed is very much not a bottleneck in Emacs. And I had a somewhat similar config for Vim. Things like quick access to docs, quick navigation (thing like running a lint program and then navigating directly to each error), quick commit, quick blaming and time traveling through the code history,…