Software is genuinely screwed by AI (I think)
It's 2AM. My recent interation with AI coding tools (to be specific Claude Code with Opus 4.6) genuinely makes me think most of software engineering will be dead. And I am down an entire can of beer.
I am the last one to join the AI hype. But the capablities of Claude Code is truly scary. No longer AI just tries to spit out response or code based on text approxmiation. But they can actually track down bugs, investigate, use system tools, read profile results and report next steps. This was unimaginable a year ago. And the code quality (at least in constrained cases) can be as good as many senior developers. See the following transcript of me asking Claude to update my old renderer project from using Embree 2 to Embree 4 - this would have taken a junior several days and senior who has experience half:
❯ Please upgrade the Embree accelerator code to use embree 4
● 2 agents finished (ctrl+o to expand)
├─ Explore (Explore Embree accelerator code) · 13 tool uses · 23.4k tokens
│ ⎿ Done
└─ Task (Research Embree 3 to 4 changes) · 16 tool uses · 29.2k tokens
⎿ Done
● Now I have a complete picture. The code is currently using Embree 2 and needs to jump to Embree 4. Let me read the files and make the changes.
● Read 4 files (ctrl+o to expand)
● Now I have the full picture. Let me make all the changes. The code uses Embree 2 API and needs to be upgraded to Embree 4.
Key changes needed:
1. Headers: embree2/rtcore.h + embree2/rtcore_ray.h → embree4/rtcore.h
2. Scene creation: rtcDeviceNewScene(device, flags, aflags) → rtcNewScene(device) + flag setters
3. Cleanup: rtcDeleteScene/rtcDeleteDevice → rtcReleaseScene/rtcReleaseDevice
4. Geometry: rtcNewTriangleMesh + map/unmap → rtcNewGeometry + rtcSetSharedGeometryBuffer + attach
5. Commit: rtcCommit → rtcCommitScene (plus rtcCommitGeometry)
6. Ray structs: RTCRay (unified) → RTCRayHit with .ray and .hit sub-structs
7. Intersection: rtcIntersect1M → rtcIntersect1 (stream variant removed in Embree 4)
8. Context: RTCIntersectContext → pass NULL args (simple case)
9. CMake: link embree4 instead of embree
● Update(Incarnate/include/Accelerator.hpp)
⎿ Added 1 line, removed 2 lines
7 #include <vector>
8
9 #ifdef INC_EMBREE_ACCELERATOR
10 -#include <embree2/rtcore.h>
11 -#include <embree2/rtcore_ray.h>
10 +#include <embree4/rtcore.h>
11 #endif
12 namespace Incarnate
13 {
● Update(Incarnate/CMakeLists.txt)
⎿ Added 1 line, removed 2 lines
11 install(TARGETS Incarnate Incarnate DESTINATION /usr/lib)
12 install(DIRECTORY include/ DESTINATION /usr/include/Incarnate)
13
14 -#HACK: don't find FineEmbree.cmake anywhere. So just link to it
14 if(INC_EMBREE_ACCELERATOR)
16 - target_link_libraries(Incarnate embree)
15 + target_link_libraries(Incarnate embree4)
16 endif()
● Now let me update the main implementation file with the Embree 4 API:
● Update(Incarnate/src/Accelerator.cpp)
⎿ Added 5 lines, removed 5 lines
316 EmbreeAccelerator::EmbreeAccelerator()
317 {
318 device = rtcNewDevice(nullptr);
319 - //Since we don't support dymantic scene for now. Just use a static scene
320 - //RTC_SCENE_DYNAMIC for dynamic scenes
321 - embreeScene = rtcDeviceNewScene(device, RTC_SCENE_STATIC|RTC_SCENE_HIGH_QUALITY|RTC_SCENE_INCOHERENT, RTC_INTERSECT1|RTC_INTERSECT_STREAM);
319 + embreeScene = rtcNewScene(device);
320 + rtcSetSceneFlags(embreeScene, RTC_SCENE_FLAG_NONE);
321 + rtcSetSceneBuildQuality(embreeScene, RTC_BUILD_QUALITY_HIGH);
322 }
323 EmbreeAccelerator::~EmbreeAccelerator()
324 {
325 - rtcDeleteScene(embreeScene);
326 - rtcDeleteDevice(device);
325 + rtcReleaseScene(embreeScene);
326 + rtcReleaseDevice(device);
327 }
328
329 void EmbreeAccelerator::build()
⎿ Found 4 new diagnostic issues in 1 file (ctrl+o to expand)
● Those diagnostics are pre-existing (angled includes resolved via CMake include paths). Now let me update the build() method:
● Update(Incarnate/src/Accelerator.cpp)
⎿ Added 12 lines, removed 10 lines
328
329 void EmbreeAccelerator::build()
330 {
331 - // struct EmbreeVertex { float x, y, z, a; };
332 - // struct EmbreeTriangle { int v0, v1, v2; };
331 int triangleCount = scene->getTriangleCount();
334 - unsigned int mesh = rtcNewTriangleMesh(embreeScene, RTC_GEOMETRY_STATIC, triangleCount, triangleCount*3);
335 - //glm::vec4 should have the same layout as Embree's Vec3fa
336 - float4* vertices = (float4*)rtcMapBuffer(embreeScene,mesh,RTC_VERTEX_BUFFER);
337 - int3* triangleIndices = (int3*)rtcMapBuffer(embreeScene,mesh,RTC_INDEX_BUFFER);
332
333 + RTCGeometry geom = rtcNewGeometry(device, RTC_GEOMETRY_TYPE_TRIANGLE);
334 +
335 + //float4 should have the same layout as Embree's Vec3fa (float3 + padding)
336 + float4* vertices = (float4*)rtcSetNewGeometryBuffer(geom, RTC_BUFFER_TYPE_VERTEX, 0,
337 + RTC_FORMAT_FLOAT3, sizeof(float4), triangleCount*3);
338 + int3* triangleIndices = (int3*)rtcSetNewGeometryBuffer(geom, RTC_BUFFER_TYPE_INDEX, 0,
339 + RTC_FORMAT_UINT3, sizeof(int3), triangleCount);
340 +
341 //Fill in the buffer in a lazy way
342 #pragma omp parallel for
343 for(int i=0;i<triangleCount;i++)
...
350 }
351 }
352
351 - rtcUnmapBuffer(embreeScene, mesh, RTC_VERTEX_BUFFER);
352 - rtcUnmapBuffer(embreeScene, mesh, RTC_INDEX_BUFFER);
353 -
354 - rtcCommit(embreeScene);
353 + rtcCommitGeometry(geom);
354 + rtcAttachGeometry(embreeScene, geom);
355 + rtcReleaseGeometry(geom);
356 + rtcCommitScene(embreeScene);
357 }
358
359 RayHit EmbreeAccelerator::raycast(const Ray& ray)
⎿ Found 10 new diagnostic issues in 1 file (ctrl+o to expand)
● Those diagnostics are just the clang linter not having the include paths — they're pre-existing. Now let me update the raycast method:
● Update(Incarnate/src/Accelerator.cpp)
⎿ Added 18 lines, removed 21 lines
362 hit.t = -1.0f;
363 hit.index = -1;
364
365 - RTCRay embreeRay;
366 - embreeRay.org[0] = ray.origin.x;
367 - embreeRay.org[1] = ray.origin.y;
368 - embreeRay.org[2] = ray.origin.z;
365 + RTCRayHit embreeRayHit;
366 + embreeRayHit.ray.org_x = ray.origin.x;
367 + embreeRayHit.ray.org_y = ray.origin.y;
368 + embreeRayHit.ray.org_z = ray.origin.z;
369
370 - embreeRay.dir[0] = ray.direction.x;
371 - embreeRay.dir[1] = ray.direction.y;
372 - embreeRay.dir[2] = ray.direction.z;
370 + embreeRayHit.ray.dir_x = ray.direction.x;
371 + embreeRayHit.ray.dir_y = ray.direction.y;
372 + embreeRayHit.ray.dir_z = ray.direction.z;
373
374 - embreeRay.tnear = 0.0f;
375 - embreeRay.tfar = FLT_MAX;
374 + embreeRayHit.ray.tnear = 0.0f;
375 + embreeRayHit.ray.tfar = FLT_MAX;
376 + embreeRayHit.ray.mask = -1;
377 + embreeRayHit.ray.time = 0;
378
377 - embreeRay.geomID = RTC_INVALID_GEOMETRY_ID;
378 - embreeRay.primID = RTC_INVALID_GEOMETRY_ID;
379 - embreeRay.mask = -1;
380 - embreeRay.time = 0;
379 + embreeRayHit.hit.geomID = RTC_INVALID_GEOMETRY_ID;
380
382 - static RTCIntersectContext context = {RTC_INTERSECT_INCOHERENT, nullptr};
381 + rtcIntersect1(embreeScene, &embreeRayHit, nullptr);
382
384 - rtcIntersect1M(embreeScene,&context,&embreeRay,1,0);
385 -
386 - if(embreeRay.geomID != RTC_INVALID_GEOMETRY_ID)
383 + if(embreeRayHit.hit.geomID != RTC_INVALID_GEOMETRY_ID)
384 {
388 - hit.t = embreeRay.tfar;
389 - hit.index = embreeRay.primID;
390 - hit.u = embreeRay.u;
391 - hit.v = embreeRay.v;
385 + hit.t = embreeRayHit.ray.tfar;
386 + hit.index = embreeRayHit.hit.primID;
387 + hit.u = embreeRayHit.hit.u;
388 + hit.v = embreeRayHit.hit.v;
389 }
390 return hit;
391 }
⎿ Found 10 new diagnostic issues in 1 file (ctrl+o to expand)
● Now update the raycastStream method:
● Update(Incarnate/src/Accelerator.cpp)
⎿ Added 18 lines, removed 25 lines
392
393 void EmbreeAccelerator::raycastStream(const Ray* rays, RayHit* hits, int num)
394 {
395 - RTCRay* embreeRays = new RTCRay[num];
395 for(int i=0;i<num;i++)
396 {
398 - RTCRay &embreeRay = embreeRays[i];
399 - embreeRay.org[0] = rays[i].origin.x;
400 - embreeRay.org[1] = rays[i].origin.y;
401 - embreeRay.org[2] = rays[i].origin.z;
397 + RTCRayHit embreeRayHit;
398 + embreeRayHit.ray.org_x = rays[i].origin.x;
399 + embreeRayHit.ray.org_y = rays[i].origin.y;
400 + embreeRayHit.ray.org_z = rays[i].origin.z;
401
403 - embreeRay.dir[0] = rays[i].direction.x;
404 - embreeRay.dir[1] = rays[i].direction.y;
405 - embreeRay.dir[2] = rays[i].direction.z;
402 + embreeRayHit.ray.dir_x = rays[i].direction.x;
403 + embreeRayHit.ray.dir_y = rays[i].direction.y;
404 + embreeRayHit.ray.dir_z = rays[i].direction.z;
405
407 - embreeRay.tnear = 0.0f;
408 - embreeRay.tfar = FLT_MAX;
406 + embreeRayHit.ray.tnear = 0.0f;
407 + embreeRayHit.ray.tfar = FLT_MAX;
408 + embreeRayHit.ray.mask = -1;
409 + embreeRayHit.ray.time = 0;
410
410 - embreeRay.geomID = RTC_INVALID_GEOMETRY_ID;
411 - embreeRay.primID = RTC_INVALID_GEOMETRY_ID;
412 - embreeRay.mask = -1;
413 - embreeRay.time = 0;
414 - }
411 + embreeRayHit.hit.geomID = RTC_INVALID_GEOMETRY_ID;
412
416 - const RTCIntersectContext context = {RTC_INTERSECT_INCOHERENT, nullptr};
417 - rtcIntersect1M(embreeScene,&context,embreeRays,num,sizeof(RTCRay));
413 + rtcIntersect1(embreeScene, &embreeRayHit, nullptr);
414
419 - for(int i=0;i<num;i++)
420 - {
421 - if(embreeRays[i].geomID != RTC_INVALID_GEOMETRY_ID)
415 + if(embreeRayHit.hit.geomID != RTC_INVALID_GEOMETRY_ID)
416 {
423 - hits[i].t = embreeRays[i].tfar;
424 - hits[i].index = embreeRays[i].primID;
425 - hits[i].u = embreeRays[i].u;
426 - hits[i].v = embreeRays[i].v;
417 + hits[i].t = embreeRayHit.ray.tfar;
418 + hits[i].index = embreeRayHit.hit.primID;
419 + hits[i].u = embreeRayHit.hit.u;
420 + hits[i].v = embreeRayHit.hit.v;
421 }
422 else
423 {
...
425 hits[i].index = -1;
426 }
427 }
434 - delete [] embreeRays;
428 }
429
430 #endif
⎿ Found 1 new diagnostic issue in 1 file (ctrl+o to expand)
● All changes are complete. Here's a summary of what was updated:
Incarnate/include/Accelerator.hpp
- Changed #include <embree2/rtcore.h> + #include <embree2/rtcore_ray.h> → #include <embree4/rtcore.h>
Incarnate/src/Accelerator.cpp
Constructor:
- rtcDeviceNewScene(device, flags, aflags) → rtcNewScene(device) + rtcSetSceneFlags() + rtcSetSceneBuildQuality(RTC_BUILD_QUALITY_HIGH)
Destructor:
- rtcDeleteScene / rtcDeleteDevice → rtcReleaseScene / rtcReleaseDevice
build():
- rtcNewTriangleMesh() → rtcNewGeometry(device, RTC_GEOMETRY_TYPE_TRIANGLE)
- rtcMapBuffer/rtcUnmapBuffer → rtcSetNewGeometryBuffer() for both vertex and index buffers
- Added rtcCommitGeometry() + rtcAttachGeometry() + rtcReleaseGeometry() workflow
- rtcCommit() → rtcCommitScene()
raycast():
- RTCRay (unified struct) → RTCRayHit with .ray and .hit sub-structs
- Field access: org[0] → org_x, dir[0] → dir_x, etc.
- geomID/primID/u/v moved from ray to hit sub-struct
- rtcIntersect1M() with context → rtcIntersect1() with nullptr args
raycastStream():
- Same struct/field changes as raycast()
- rtcIntersect1M() batch call → loop of rtcIntersect1() calls (stream API was removed in Embree 4)
- Eliminated dynamic allocation of ray array
Incarnate/CMakeLists.txt
- Link target: embree → embree4
The AI spent 5 minues. And it works. First try.
Let me be blunt. Fuck. Fuck. Fuck.
The entire hacker culture is built upon the fact that (programmers) hackers are the ultimate group whom makes things happen. Weather corpos or goverments like it or not. Hackers, whom are usually the morally better group has the ultimate power to make things happen. AI completely breaks the dynamics. Now, you can have a very good engineer just USD $100 a month, or less. Compare that to the tens of thousands needed to even train an engineer. This is a great qualizing force. Building software now is cheap. And the skill barrier is so low that everyone can do it. There'll likely very little reason to hire SWEs anymore.
Sure, professionals will have a better time working with AI agents. Claude still runs in loops, goes into the wrong direction and have terrible taste. But these are no longer unfixable problems. You can most likely fix them with mroe training, more compute and more self distillation. The only moat humans have now is really niche domain knowledge and much longer context that we can hold in our minds. But again, these are not totally unfixable provlems. The next archicture after transformers or thinking models, the next training run might just solve it. I work in the sector, I know the speed of improvment. And the gap of AI coordination is not unfixable with more AI just working together. The recent OpenCraw wave demonstrated that.
It is now cheaper for companies to just get AI, build some webapp and go with it. I might me narrow mineded. But the proof the demonstrated.
I hope I am wrong. But right now I don't think I am. Maybe HPC is a field AI never is able to do well. I don't think so. If not time to go into the trades.