Showing posts with label security. Show all posts
Showing posts with label security. Show all posts

Monday, 14 September 2026

Artificial Intelligence and Its Effect on Entry-level Hiring

Artificial Intelligence, despite not being fully-formed, is no longer merely threatening to shake up the labor market. By most accounts, it has already happened. Even if one takes "AI-washing" (where companies use AI disruption as a convenient scapegoat rather than a genuine reason for layoffs) into account, the fact of the matter is that two classes of tech workers are currently facing extinction.

The first are the leetcoders or hackathoners, who have spent their entire careers training to be the fastest code typers and problem-solvers possible. Entire movies have been made about these rockstars... and now they're finding out that they'll never produce code faster than a machine. I'm trying to find it in my heart to produce some sympathy, but honestly they should have seen this coming. This industry automates so many things; why not coding?

Very screwed.

And if your entire value proposition has been speed, I don't know what to tell ya, son, you're more screwed than a plank at a carpenter's convention.

The second class, of course, are entry-level tech workers.

Entry-level tech workers

The moment LLMs started being capable of churning out subpar code at light speed, at a fraction of the cost of hiring cheap slave labor fresh-faced geeks, the position of these tech workers was in jeopardy. And the LLMs aren't even all that subpar at the time of this writing. I have significantly more sympathy for this demographic than for the leetcoders, due to the simple fact that they never asked for any of this. They didn't make the conscious career mistake of maximizing for typing speed. They learned the fundamentals and did the work, but their careers look dead in the water before even being given a chance to pay their dues.

The bigger concern - one that's already been repeated ad nauseam, but bears repeating nonetheless - is that if entry-level workers aren't even given the opportunity to level up, they aren't going to grow into senior-level workers. And the senior-level workers won't be around forever.

Space to grow.

Entry-level workers should be given the space to make mistakes and grow. Already, "civilians" with LLMs are finding out that being able to build "working software" isn't quite the same thing as a qualified software developer producing something with a baseline level of engineering rigor. For example, I've come across civilian-produced apps that lack Cross-site Request Forgery protection or even expose database ids right in the URL. To provide non-technical readers the proper context, CSRF hasn't been in the OWASP Top Ten for years, but that doesn't mean it's no longer a threat - it just means that enough people have guarded against it that it's no longer a popular successful attack vector. Thus, this represents a step back. Civilians and entry-level workers alike, make mistakes; but unlike civilians, entry-level workers are professionally interested in improving.

Think about it - why is someone like me occupying this niche in the food chain? Because I'm so damn special? There is nothing special about me. I started life as a stupid kid like most of you schmucks reading this, and figured my way out over time. I spent years making mistakes and learning from them, and learning the right questions to ask. I have the scars that neither the entry-level tech worker or the non-tech worker do. More importantly, I was allowed to make those mistakes, in addition to performing the countless (entry-level, often repetitive) tasks that LLMs can now do... but precisely due to having developed that muscle memory, I'm now qualified to validate the LLM's output. If entry-level workers don't have even these tasks, how else will they grow?

Employers will say that it's not their responsibility to train the next generation of developers. Their primary responsibility is to their organizations. That's completely fair.

Who's the bad guy in this story, then?

Well, it's not AI. AI exists, but AI is not the one making the decision not to hire young developers. AI merely makes that decision more of a no-brainer.

It would be easy to paint employers as the bad guys here. Lazy too, and that's why I'm not going to do it.

Sure, they're the ones opting to adopt AI at the expense of entry-level (and even more senior) workers... but it's mostly in response to what their competitors are doing. Some of these employers don't have the luxury of thinking ten years into the future and investing in youth now. They're in the position where they know it's a bad idea, and they have to go ahead anyway. Shit, even some giants in Silicon Valley have to make moves that mirror the competition; what chance do the rest have, really?

The bad guy.

No one is the bad guy. It's just evolution. Insisting there has to be a bad guy is like calling the meteor that deleted all the dinosaurs way back then, the villain.

From where I sit, LLMs are the latest in a long line of evolutions in tech history. Significant? Of course. World-shattering? Whose world, exactly? People like me are on our way out. We don't exactly have much skin in the game.

Crap advice

In these times of uncertainty, the only certainty there's to be found seems to be from the voices of older workers, with a great deal of confidence as to how these new treacherous waters should be navigated. Toughen up. Pivot. Find your spine. Work hard and grow your network.

Excuse the fuck out of me. Toughen up? That's like telling people to learn to swim when a tsunami is crashing onto the beach. Sound advice under most circumstances, but useless for immediate survival.

Learn to swim!

One of the things I really can't stand about older tech workers like myself, or even just older workers my age in general, is that they seem to think that their age makes them intrinsically superior to the younger generation. Don't get me wrong; older and experienced workers come with a good deal of advantages, but it doesn't make us automatically qualified to advise the next generation on how to conduct their careers.

I mean, do you have experience as a young adult trying to find work? Sure. Do you have experience as an older worker trying to find work in 2026? Maybe. Do you know what it's like to be a young adult, trying to find work in 2026? No, pal, you don't fucking know. Obviously, you can't have firsthand experience. And unless you shut your mouth and listen more, you're not likely to have secondhand experience either.

Also, consider this: whatever know-how you may have accumulated over your years of experience has now been commoditized with a correctly-worded prompt. Knowing the right questions to ask is a whole other matter, of course. In fact, that is possibly the only thing separating you from the tech cosplayers with their LLMs pretending that they're the equivalent of fully-trained engineers (some of whom might actually even believe it, poor bastards). But at some point, these young tech workers are going to figure out the right questions to ask. They'll have your know-how, but they have youthful energy and enthusiasm. You have high cholesterol levels and creaking knees. Do the math.

What lies in store? Damn if I know.

I wouldn't want to be a young tech worker in 2026. Not only is the ground shaky, you've got people with the sheer audacity to tell you what you should be doing, in an arena they haven't fought in before. An unenviable position.

I have no answers, not even bad ones. If I did, people would be paying me for my opinion, not reading it for free on this blog.

Have an AI-dealistic day!
T___T

Monday, 1 June 2026

Bolt CEO's dangerous logic for axing his entire HR Team

It's been a couple weeks since I last heard the news that the entire HR Team at Bolt was fired by CEO Ryan Breslow.

Now I'm not a fan of HR, and I like to warn people that mistaking HR as your friend is a mistake they should make only once, if at all. Hell, "nobody at work is your friend" is a good principle to have, and HR should probably head that list.


But anyone expecting me to join the pile-on and gloat about "HR finally getting a taste of their own medicine"? Sorry lads, I'm going to have to disappoint you today. This is a dodgy move at best.

What happened

Bolt was valued at around $11 billion in 2022 when Breslow stepped away, and lost up to 90% of its valuation by 2026. Breslow returned to make sweeping changes, starting with removing about 30% of staff, including the HR function.

Breslow was on record glibly saying the following.
"We had an HR team, right? And that HR team was creating problems that didn't exist. And those problems disappeared when I let them go."

Don't know about you, but this simultaneously caused me amusement and discomfort. It reminded me of a joke I used to make as a smoker.
"I've been smoking for years, but when I read about all the different toxins I've been inhaling into my body, I got so disturbed that I stopped reading."

Turn that racket off!

I mean, would you turn off the fire alarm just because it made a loud distracting sound every time there was a fire? Or remove the brakes on your car to make it incapable of slowing down?

Yes, that's the TeochewThunder equivalent of sticking your head in the sand.

Another disturbing idea

Breslow was also quoted as saying, sounding somewhat conciliatory.
"Those HR professionals have really important insights when you're in a peacetime at a larger company. But we're a remote company, it's not like - a lot of the potential issues that you would have in a workplace don't really exist because you're not in the same room as somebody."

Did our boy just say we don't really need HR if we're remote? Maybe not, but it sounded an awful lot like it. And in case that's the takeaway anyone gets from this, I'm going to shut it down right now.

HR problems don't exist only if people are in the same room together. Remote workers still have to get paid, appraised and onboarded. They still interact with other workers, even online. Company leadership may still, in their zeal, make errors in judgement they need to be warned about. And in certain cases, HR problems get more intense precisely because it's remote. Workplace bullying, for example. I've encountered wankers who became even bigger wankers simply because being remote meant they weren't in immediate danger of being bitch-slapped for talking shit taken to task for being impolite. Come now, we all know someone like that.

What did he just
say to me?!

The only way remote work lessens the need for HR would be if HR's only function was just to look pretty and ask how everyone's day was. And that's not how it works. Not even a little bit.

So no... HR does not become less relevant because the company structure is remote. Sometimes, in those cases, HR becomes more relevant.

The real danger

Let's give Breslow the benefit of the doubt. Let's say HR was really creating problems where there were none, or making issues out of non-issues. Mountains out of molehills. Playing office politics, or worse, identity politics. Overreaching like they were cosplaying Dhalsim in Street Fighter.

Maybe. And even then, saying "they made problems so I got rid of them, problem solved" is a dangerous path to go down.

The point is, what often looks like non-issues slowing things down unnecessarily, are actual problems that laypersons just aren't equipped to handle. And let me be clear; this is not about defending HR.

Not interested in
defending HR.

What if it was an entire team of software developers who were fired because business people started thinking that software devs and their trifling concerns were slowing shit down? We've got LLMs that can generate code now, so business people don't actually need techies to write code.

However, business people are not software engineers, and being able to produce code doesn't magically make them so. Business people have largely the same concerns as software developers - aesthetics, functionality, compliance, security, maintainability - but ask a business person and software developer to rank all these in order of importance, and their answers would look very different. Go ahead, ask business people what they'd do if you told them implementing unit tests would add another week to deployment time at minimum. I'm not saying that all business people would give you an answer that would have you internally questioning your life choices, but the fact is that business and tech people have very different priorities. That tends to introduce resistance into the decision-making process.

What if people started thinking that, armed with LLMs, they could just replace software engineers and get rid of that resistance? Y'all know it's a matter of time someone starts going down this road, if they haven't already. I've seen business people make apps using LLMs, with sometimes comical results. But let's not pretend that eventually replacing techies isn't on the cards.

Au Revoir, Bolt HR!

Still think the firing the entire HR team isn't a big deal? You're right, it's probably not. But the reasoning - that's something we need to look at.

Time for me to Bolt,
T___T

Monday, 4 May 2026

The Case of the Awkward Tech Interviewee

It's been a while since I attended a job interview. As an applicant, anyway.

Recently, I was on the other side of the interview, reviewing candidates for a tech position within my company. The interviewee in question was a nervous-looking Malaysian. There wasn't anything that really stood out, except for his awkwardness. Which, in itself, was a problem. You see, for tech positions, sometimes one can get away with not being able to communicate that well. The stereotype of socially-inept but brilliant techies exists for a reason.

Brilliant but
awkward?

However, this particular position was for a leadership role. And lack of communication skills just wasn't going to fly. I could tell that my co-interviewer had already written our applicant off and was about to call it in, but I persisted. We'd already scheduled and allotted the time; I figured that I might as well develop the muscle memory I needed for being an interviewer.

Besides, it could have been just nerves. Some people have told me I can be intimidating, a statement I consider laughable. Please, look at this face. Nothing about me is remotely intimidating.

Either way, I decided to give him a question he could answer. I asked him to explain what he would do to combat SQL Injection. His answer, predictably, was "Stored Procedures". My follow-up, probably just as predictably, was why Stored Procedures? What do Stored Procedures do that normal SQL queries don't?

He stammered. Hemmed. Hawed.

After a few minutes, I put him out of his misery. I told him to Google the term "paramterized queries". And then told him that in future, if anyone ever asked him a question like that again, he should refer to that, rather than simply say "Stored Procedures". It would be a better, more complete, more correct answer. And more importantly, it would be an answer that instilled confidence in the asker of the question, confidence that the answerer knew his shit.

After we were thanked him for his time and bade him goodbye, my co-interviewer turned to me and asked me why I'd spent so much time on this guy. Did I see some potential that he'd missed?

I replied, simply, that we obviously weren't going to give him the job... so I might as well give him something.

People commented on my perceived generosity, or at least, on me "paying it forward". I don't think either is true. This was decidedly not about making the tech industry a better place, or even giving back.

Back then...

Upon further reflection, something similar happened to me in the days when I was the applicant. There were times when I sensed I had failed the interview, and when the interviewer asked if I had any questions, I ventured out of the box.

I asked for advice. And this paid off in spades. They proceeded to tell me more or less where I'd gone wrong. We all knew I wouldn't make it past this round, but they at least thought I deserved a fighting chance... somewhere else. They wanted me to succeed, again, somewhere else. I guess it helped that His Teochewness has such a winning personality.

Seeking answers.

I think that helping candidates along is a very natural instinct on the part of any tech interviewer who takes their job seriously. Of course, it helps if the applicant doesn't piss them off. One may object on the grounds that this helps applicants cheat on their next tech interview. That's only true if they get asked the exact same question next interview. Also, let's be real, answers are all over the internet.

And from a cynical point of view, even if it's true that you're enabling inferior candidates to "cheat", they'd likely be "cheating" at the interviews of your competitors. Nobody really needs me to explain this one now, do they?

It's probably also true that techies generally like to look out for other techies. Some little tribal instinct there. Us geeks against the laypersons.

To conclude...

Interviews need not be an adversarial exercise. Your interviewers want you to succeed, or at least not turn out to be a total waste of time. Selfishly, because they already invested the time and energy into prepping for this interview, and conducting it. From that point of view, they get nothing if you turn out to be a dud.

Therefore, they don't need to love you. They just need to not severely dislike you. And the rest takes care of itself.

Without question,
T___T

Tuesday, 17 March 2026

The Bowknot Analogy

In seamanship, slipped knots are common, such as the Slippery Sheetbend, the Slippery Reef Knot, the Slippery Bowline, and so on. This is so that the knot in question has a quick-release mechanism. It is accomplished by doubling back the end of the rope under the last tuck, so that pulling on that end immediately undoes the knot.

Slippery Reef Knot

The Slippery Reef Knot is one such example. If we pull on the "slippery" end, it undoes the knot. If we pull on the other end, it tightens the knot.

But what if we made both ends slippery? Then, dear readers, we have what is known as a Bowknot.

Bowknot

Just like the kind you finish off wrapped presents with, or tie your shoelaces with.

The misconception here is that making both ends slippery, doubles the "slipperiness" of the knot. Not so! What has happened now is, instead of having one end being a quick-release mechanism, you now have two quick-release mechanisms. Pulling on either one end unties the entire knot. The quick-release mechanism functions the exact same way, and neither slippery end contributes to the other's quick-release mechanism.

Similarly in tech security...

You may have heard me speak of 2FA before. 2FA is an acronym for Two-factor Authentication. Meaning, a system that requires the user to authenticate in two different ways. Having two password fields does not count, because it just means that the user authenticates two times, the same way.

Biometrics!

Using a password and a fingerprint scan combination would count. Because then the user would have to authenticate two different ways.

Let's tie these two situations together (pun intended). Someone tying a Bowknot expecting the knot to become more slippery than a Slippery Reef Knot, is like adding an extra password field to a system expecting to make it more secure. It doesn't. It only makes the system more inconvenient for the user. If attackers can bypass one password field, they can bypass two.

The Takeaway

More of the same does not mean more of the benefits. That's really the common thread here. In security, the nature of the attacks are wide and varied. So, too, must your defenses be.

Taking a bow,
T___T

Tuesday, 23 December 2025

Web Tutorial: Ruby On Rails Xmas Poll (Part 3/4)

We have a form, and now it's a matter of submitting it. In the Submit action of the Poll controller, we want to collect this data and send it to Oracle APEX.

We'll have to first massage this data into a payload to send. For that, we declare answers as the the collection of all the HTML elements with answers as the name. Then we declare payload as an object with one property, answers. The value of that, will be answers.

app/controllers/poll_controller.rb
def submit
    answers = params[:answers]
    payload = { answers: answers }

end


We then use HTTParty to POST, just like we used it to send a GET request earlier. The base URL is the same - we'll use ORDS_API_URL. For the body, we use payload after running the to_json() method on it, to convert it to a JSON object. And because of this, we should specify that it's JSON in the headers object. The result is returned in the variable response.

app/controllers/poll_controller.rb
def submit
    answers = params[:answers]
    payload = { answers: answers }

    response = HTTParty.post(
        ORDS_API_URL,

        body: payload.to_json,
        headers: {
            "Content-Type" => "application/json"
        }
    )
end


Now, if it's successful, the code property of response will be 200. In that case flash a green success message. If not, flash a red error message.

app/controllers/poll_controller.rb
def submit
    answers = params[:answers]
    payload = { answers: answers }

    response = HTTParty.post(
        ORDS_API_URL,
        body: payload.to_json,
        headers: {
            "Content-Type" => "application/json"
        }
    )

    if response.code == 200
        flash[:notice] = "Submission successful!"

    else
        flash[:alert] = "API error."

    end
end


When all's said and done, use the redirect_to statement to return to root_path, which is the poll form.
app/controllers/poll_controller.rb
def submit
    answers = params[:answers]
    payload = { answers: answers }

    response = HTTParty.post(
        ORDS_API_URL,
        body: payload.to_json,
        headers: {
            "Content-Type" => "application/json"
        }
    )

    if response.code == 200
        flash[:notice] = "Submission successful!"
    else
        flash[:alert] = "API error."
    end

    redirect_to root_path
end


Next, let's go back to Oracle APEX. Remember we created the GET handler for the API endpoint "poll/:id"? Well, now create a POST handler.


Make sure the id variable is defined, and we tell Oracle APEX that it's to be found in the URL.



Before we examine the PL/SQL code for the handler, this is the shape of the data that will be sent, as an example.
{
  "Answers": {
    "1": "4",
    "2": "5",
    "3": "1",
    "4": "2",
    "5": "1",
    "6": "4",
    "7": "4",
    "8": "3",
    "9": "3",
    "10": "5"
  }
}


We have a DECLARE, BEGIN and END statements. After DECLARE, we declare l_request_body_clob as a CLOB object. A CLOB is a Character Large Object, which pretty much describes the data that will be sent to Oracle APEX via the form. Then we declare l_keys as an array used to store strings. (If you're curious, the "l" prefix is used to say "local". Seems superfluous, but it's Oracle's convention, so...)
DECLARE
    l_request_body_clob CLOB;
    l_keys APEX_T_VARCHAR2 := APEX_T_VARCHAR2();
BEGIN

END;


body_text refers to the data sent in the form, via the API endpoint. This value is assigned to the variable l_request_body_clob.
DECLARE
    l_request_body_clob CLOB;
    l_keys APEX_T_VARCHAR2 := APEX_T_VARCHAR2();
BEGIN
    l_request_body_clob := :body_text;
END;


Then we use the parse() method of the APEX_JSON object, passing in l_request_body_clob as the p_source parameter's value. This, in effect, parses the form body data.
DECLARE
    l_request_body_clob CLOB;
    l_keys APEX_T_VARCHAR2 := APEX_T_VARCHAR2();
BEGIN
    l_request_body_clob := :body_text;

    APEX_JSON.parse(p_source => l_request_body_clob);
END;


Now for l_keys. We want to get the keys from the answers object. So we use the get_members() method of the APEX_JSON object (which already parsed the form data) and specify that the name of the object is "answers" by setting that as the parameter value of p_path. This in effect produces an array of all the keys in the form data, and binds that value to the array l_keys.
DECLARE
    l_request_body_clob CLOB;
    l_keys APEX_T_VARCHAR2 := APEX_T_VARCHAR2();
BEGIN
    l_request_body_clob := :body_text;

    APEX_JSON.parse(p_source => l_request_body_clob);

    l_keys := APEX_JSON.get_members(p_path => 'answers');
END;


To be safe, we have an IF block to check that l_keys is a valid non-empty array. Then we iterate through l_keys.
DECLARE
    l_request_body_clob CLOB;
    l_keys APEX_T_VARCHAR2 := APEX_T_VARCHAR2();
BEGIN
    l_request_body_clob := :body_text;

    APEX_JSON.parse(p_source => l_request_body_clob);

    l_keys := APEX_JSON.get_members(p_path => 'answers');

    IF l_keys IS NOT NULL AND l_keys.COUNT > 0 THEN
        FOR i IN 1..l_keys.COUNT LOOP

            DECLARE

            BEGIN


            END;

        END LOOP;
    END IF;
END;


We'll declare the serial number and answer here, in the variables l_serial_no and l_answer_value respectively. We know that those are just numbers and they won't go above 10, so "VARCHAR2(2)" is safe enough.
DECLARE
    l_request_body_clob CLOB;
    l_keys APEX_T_VARCHAR2 := APEX_T_VARCHAR2();
BEGIN
    l_request_body_clob := :body_text;

    APEX_JSON.parse(p_source => l_request_body_clob);

    l_keys := APEX_JSON.get_members(p_path => 'answers');

    IF l_keys IS NOT NULL AND l_keys.COUNT > 0 THEN
        FOR i IN 1..l_keys.COUNT LOOP
            DECLARE
                l_serial_no VARCHAR2(2);
                l_answer_value VARCHAR2(2);
            BEGIN

            END;
        END LOOP;
    END IF;
END;


Then we assign the value of the current element of l_keys, to l_serial_no. And we use the get_varchar2() method of APEX_JSON, again using "answers." and the current value of l_serial_number ("||" is actually concatenation in PL/SQL, so we're trying to read the value of answers.1, answers.2, and so on.) as the value of p_path, and assign the value to l_answer_value. Phew! That was a mouthful. But you get the idea... I hope.
DECLARE
    l_request_body_clob CLOB;
    l_keys APEX_T_VARCHAR2 := APEX_T_VARCHAR2();
BEGIN
    l_request_body_clob := :body_text;

    APEX_JSON.parse(p_source => l_request_body_clob);

    l_keys := APEX_JSON.get_members(p_path => 'answers');

    IF l_keys IS NOT NULL AND l_keys.COUNT > 0 THEN
        FOR i IN 1..l_keys.COUNT LOOP
            DECLARE
                l_serial_no VARCHAR2(2);
                l_answer_value VARCHAR2(2);
            BEGIN
                l_serial_no := l_keys(i);
                l_answer_value := APEX_JSON.get_varchar2(p_path => 'answers.' || l_serial_no);
            END;
        END LOOP;
    END IF;
END;


And we write an INSERT statement that adds a row with the values of l_serial_no and l_answer_value. Because QUESTION_SERIAL_NO is an integer, we need to use the TO_NUMBER() function on l_serial_no. POLL_ID will be the variable id in the POST handler.
DECLARE
    l_request_body_clob CLOB;
    l_keys APEX_T_VARCHAR2 := APEX_T_VARCHAR2();
BEGIN
    l_request_body_clob := :body_text;

    APEX_JSON.parse(p_source => l_request_body_clob);

    l_keys := APEX_JSON.get_members(p_path => 'answers');

    IF l_keys IS NOT NULL AND l_keys.COUNT > 0 THEN
        FOR i IN 1..l_keys.COUNT LOOP
            DECLARE
                l_serial_no VARCHAR2(2);
                l_answer_value VARCHAR2(2);
            BEGIN
                l_serial_no := l_keys(i);
                l_answer_value := APEX_JSON.get_varchar2(p_path => 'answers.' || l_serial_no);

                INSERT INTO POLL_RESULTS (POLL_ID, QUESTION_SERIAL_NO, RESULT)
                VALUES (:id, TO_NUMBER(l_serial_no), l_answer_value);
            END;
        END LOOP;
    END IF;
END;


The we use the open_object(), write() and close_object() of APEX_JSON to set status and message as a response.
DECLARE
    l_request_body_clob CLOB;
    l_keys APEX_T_VARCHAR2 := APEX_T_VARCHAR2();
BEGIN
    l_request_body_clob := :body_text;

    APEX_JSON.parse(p_source => l_request_body_clob);

    l_keys := APEX_JSON.get_members(p_path => 'answers');

    IF l_keys IS NOT NULL AND l_keys.COUNT > 0 THEN
        FOR i IN 1..l_keys.COUNT LOOP
            DECLARE
                l_serial_no VARCHAR2(255);
                l_answer_value VARCHAR2(4000);
            BEGIN
                l_serial_no := l_keys(i);
                l_answer_value := APEX_JSON.get_varchar2(p_path => 'answers.' || l_serial_no);

                INSERT INTO POLL_RESULTS (POLL_ID, QUESTION_SERIAL_NO, RESULT)
                VALUES (:id, TO_NUMBER(l_serial_no), l_answer_value);
            END;
        END LOOP;
    END IF;

    APEX_JSON.open_object;
    APEX_JSON.write('status', 'success');
    APEX_JSON.write('message', 'Answers processed successfully');
    APEX_JSON.close_object;

END;


Then we have a provision for if anything goes wrong.
DECLARE
    l_request_body_clob CLOB;
    l_keys APEX_T_VARCHAR2 := APEX_T_VARCHAR2();
BEGIN
    l_request_body_clob := :body_text;

    APEX_JSON.parse(p_source => l_request_body_clob);

    l_keys := APEX_JSON.get_members(p_path => 'answers');

    IF l_keys IS NOT NULL AND l_keys.COUNT > 0 THEN
        FOR i IN 1..l_keys.COUNT LOOP
            DECLARE
                l_serial_no VARCHAR2(255);
                l_answer_value VARCHAR2(4000);
            BEGIN
                l_serial_no := l_keys(i);
                l_answer_value := APEX_JSON.get_varchar2(p_path => 'answers.' || l_serial_no);

                INSERT INTO POLL_RESULTS (POLL_ID, QUESTION_SERIAL_NO, RESULT)
                VALUES (:id, TO_NUMBER(l_serial_no), l_answer_value);
            END;
        END LOOP;
    END IF;

    APEX_JSON.open_object;
    APEX_JSON.write('status', 'success');
    APEX_JSON.write('message', 'Answers processed successfully');
    APEX_JSON.close_object;

    EXCEPTION
        WHEN OTHERS THEN

            APEX_JSON.open_object;
            APEX_JSON.write("status", "error");
            APEX_JSON.write("message", "PL/SQL Error: " || SQLERRM);

            APEX_JSON.close_object;
END;


Time to test this! Fill in the poll and click SEND.


You should see this!


And the results in Oracle APEX's database, in the POLL_RESULTS table!


One more thing...

An anti-CSFR token is very easy to apply in Ruby On Rails. Just go to this file. When you rerun your form, you should see a hidden field if you view the source. Everything else is taken care of, including the validation.

app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
    allow_browser versions: :modern
    protect_from_forgery with: :exception
end


Next

Viewing the results, and testing.

Sunday, 7 December 2025

Idempotence in CRUD operations

Hello, readers. Today we're going to talk about idempotence.

The concept of idempotence is paramount in database security. By saying that, of course, I feel compelled to explain what idempotence is. That term tends to come up a lot with regard to data transactions. The official definition is as follows, with regard to data transactions.
Idempotency is the property of an operation where performing it multiple times produces the same result as performing it once.


What that means is that if you apply an operation over and over, and it makes no difference to the database after the first time, that operation is idempotent.

With regard to CRUD (CREATE, READ, UPDATE, DELETE) operations, it's important to understand which ones are idempotent, and which ones run the risk of undesirable results if accidentally executed multiple times. 

Please note that code examples are in MySQL.

CREATE

Idempotence: No (except in special circumstances)
CREATE operations are not idempotent by default. If you run a CREATE operation multiple times, you are going to get multiple created rows.

INSERT INTO categories (name, description) VALUES ("Cat A", "test")


Running this three times will result in these three rows being added.
id name description
1 Cat A test
2 Cat A test
3 Cat A test


What if the design of the table was structured this way? What if name was supposed to be unique?
CREATE table categories (
  id INT AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(100) UNIQUE,
  description TEXT
)


Being unique.

In that case, that particular CREATE operation would be idempotent because the database simply wouldn't allow subsequent CREATE operations with the same values in the name column.
id name description
1 Cat A test


Also, if you defined an UPSERT operation instead of a CREATE, the record would only get inserted the first time. Subsequent times, the record would already exist, and therefore it would get updated... with the same values. Thus, making it idempotent. There is an exception to this exception, but that will be discussed in the UPDATE operation further down.
INSERT INTO categories (name, description)
VALUES ("Cat A", "test")
ON DUPLICATE KEY UPDATE
description = VALUES(description);


READ

Idempotence: Yes
This is the most straightforward operation in the sense that it is always idempotent. The first READ request makes no change to the database. Subsequent READ requests also make no change.

SELECT * FROM categories WHERE name = "Cat A"

READ operations are not
about changing data.

No exceptions to the rule, no nothing. This is as open-and-shut a case of idempotence as you're ever going to get in the world of computer science.

UPDATE

Idempotence: Yes (except in special circumstances)
In most cases, running an UPDATE operation on a specific record multiple times, has no effect beyond the first time. It will end up updating that record to the same values. In the query below, row id 1's name and description values would just keep being updated to "Cat A" and "test", which is basically no change.

UPDATE categories
name = "Cat A",
description = "test"
WHERE id = 1


Thus, this is idempotent. Unless...

What if the table had an Audit Field that tracked when the record was updated?
CREATE table categories (
  id INT AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(100),
  description TEXT,
  updated DATETIME
)


And what if the UPDATE operation was like this? Then the updated field would be a different value every time this operation was run, and therefore it would no longer be idempotent!

Time being tracked.


UPDATE categories
name = "Cat A",
description = "test"
updated = now()
WHERE id = 1


Also, what if the UPDATE operation wasn't on a specific row? This query updates all rows where the name field fits a particular condition. Kind of like dropping a bomb on an entire country instead of surgically taking out one terrorist. (Boy, this got dark)

UPDATE categories
name = "Cat A",
description = "test"
WHERE name like "%Cat%"


What if, just after the UPDATE operation above was run, a row was coincidentally inserted?
INSERT INTO categories (name, description) VALUES ("Cat B", "test")


Then we'd have two rows like this...
id name description
1 Cat A test
2 Cat B test


...but if the UPDATE operation was run again, this would be the result!
id name description
1 Cat A test
2 Cat A test


So, to conclude, in principle, the UPDATE operation is idempotent. But only if the values updated are absolute, and only if a very specific record is being updated.

DELETE

Idempotence: Yes (except in special circumstances)
In principle, DELETE operations are idempotent. After all, you can only delete a record successfully, once. After that, it no longer exists, so rerunning the same DELETE operation, achieves nothing. To employ a grim analogy, you can't kill someone twice.

You can only kill
someone once.

DELETE FROM categories WHERE id = 1


However, the concerns that exist for the UPDATE operation also exist for the DELETE operation. What if the DELETE operation wasn't on a specific row? This deletes all rows where the name field fits a certain filter.
DELETE FROM categories WHERE name LIKE "%CAT%"


And if, after the first DELETE operation, a record was added like so, rerunning the above DELETE operation would remove this row!
INSERT INTO categories (name, description) VALUES ("Cat B", "test")


In conclusion

A lot of what I've outlined is context. In simple cases, the answer of idempotence are similarly straightforward. But this is software development, where things are rarely that simple.

Wishing you a categorically good day,
T___T

Thursday, 18 September 2025

Computer Science Degrees aren't needed for tech, but not because of Artificial Intelligence

It was a nice Sunday afternoon when I came across this quote by Anton Osika, CEO of the AI tech company known as Lovable. In it, he made a claim that no Computer Science Degrees are needed to break into tech, now that we have Artificial Intelligence and LLMs. In the interest of keeping it real, I bristled a little at that.

Lovable has no
use for Degrees.

Not because I disagree; in fact I've often said much the same myself - that a Degree just isn't that important in terms of the actual skills required to be a software developer. No, it was more the premise I disagreed with, rather than the conclusion.

My position is that you should be able to get into tech without a Computer Science Degree... with or without AI.

Osika's Conclusion

Academically, I'm pretty decorated myself. Over the years, I've earned a Degree and multiple Diplomas in various tech disciplines. I've forgotten more than most of these youngsters ever learned, and that's not an empty boast - just not a very useful one considering how fast tech evolves.

But none of that is what qualifies me to write software. None of that makes me any more qualified than someone who picked up a book one day and spent the next several thousand hours working at the craft. What qualifies me is the experience I've picked up along the way. The things I've learned by doing.

Learning by picking
up a book.

If employers are willing to give people the chance to learn despite the lack of a Computer Science Degree, those who are genuinely interested will pick it up, and those who aren't, won't. That's the nature of the beast. A Computer Science Degree, or lack thereof, has precious little to do with it. Does it help? Oh, undoubtedly. But it's not the be-all-end-all.

I think about all the things learned during while pursuing my various tech qualifications. How much of it do I really use in my work? Pretty sure I don't use Boolean Algebra. Or Predicate Logic. Or the ability to name all seven layers of the OSI Model. That's not to say everything is useless. Sure, it's nice to know the formal terms when describing software vulnerabilities. It's great to be able to properly verbalize the various states of normalization in a database. But is it necessary as long as one has the know-how?

Therefore, in that sense, Osika is absolutely right. No Computer Science Degrees are necessary to engage in the activity of writing software, and to suggest otherwise is preposterous.

Osika's Premise

The claim was that one does not need a Computer Science Degree to develop software... and that this was due to the power of AI tools. My position is that with or without AI tools, one does not need a Computer Science Degree to develop software. That is because, as mentioned earlier, the skills needed to develop software don't necessarily come from a Computer Science Degree.

However, whether one gets those skills from a Computer Science Degree or otherwise, those skills still need to be learned. The experience still needs to be earned and accumulated. All this makes up the developer's foundation.

Putting in the hours.

It's true that the AI tools may contain a lot of knowledge... but without the foundation, a Vibe Coder can't harness that knowledge fully. I have a foundation that, while not perfect, lets me know what I don't know. I'm not saying that the Duning-Kruger Effect doesn't exist at all for someone like myself, but it is a far larger problem in the case of someone without a foundation that wishes to use AI to create software.

You can't, after all, take control of the process without knowing what to take control of. You can't close up security holes without knowing where they could appear. Sure, one could simply command AI to do all this... but you know who else does all this? Managers. Project Leads. Business guys. People who helm development teams made up of actual human techies. If you tell AI to do the work of software developers, this doesn't make you a software developer; the same way that passing instructions down to actual software developers doesn't make the Manager a qualified software developer.

To be fair, Osika's statement was about a general career in tech, and not necessarily specifically a software developer. But the fact remains is that AI has nothing to do with the ability for anyone to have a tech career. The fact someone is now instructing an AI instead of a human being, does not qualify Osika's statement. People have had the ability to instruct other people since slavers were building pyramids, and the existence of AI makes not a whit of difference in this regard.

The (un)Lovable Conclusion

The claim that one can have the know-how of a seasoned developer just by using AI tools, without having to put in the work, is nothing more than a seductive lie. After all, who doesn't want something without having to put in any work for it?

Osika's words are getting traction though, especially with people who want to believe this. And let's face it - Osika's the CEO of a notable tech company and I'm a painfully average software developer with a blog and an opinion. Thankfully, I'll probably be out of the industry before these dangerous ideas truly take hold.

Yours lovably,
T___T

Saturday, 19 July 2025

Five Reasons to learn Web Development in 2025

Recent events such as the rise of generative AI, have made tech work a little less attractive than it used to be. Web development, in particular, has suffered. That's probaby because a large chunk of web development is automatable, and even before AI came on the scene, there had been numerous tools such as Content Management Systems and Low-code development platforms.

Thus, web development being automated by AI was par for the course.

Robots writing websites.

Still, not all is lost. While web development might have lost much of its luster, there are still good, strong reasons to pick it up in ones tech career. Unlike the tech reporters and HR executives who write listicles like these, I have actually been a web developer before. I speak from experience, my dudes. And following are some of the most compelling reasons I had, in no particular order of importance, for going down this path.

1. No complicated installation

Ever tried to learn a language like PHP or Java? Every single one of these languages requires you to set up some kind of compiler or interpreter environment. PHP requires an Apache server. Java needs the Java Runtime Environment. You can write all the code you want, but until the code gets compiled or interpreted by the environment that you have to install and set up, you're not getting even a Hello World program done.

All you need is a browser.

HTML, CSS and JavaScript, however, do not. All of them already run in any major browser - Firefox, Chrome, and so on. In effect, the environment is right there for you.

This is not to say that you will never need to do any complicated installation. But for the basic building blocks - again, HTML, CSS and JavaScript - of web development, you don't. You will need to do that when you want to pick up a server-side language and maybe databases and definitely for the NodeJS style of development. But for basic stuff? Even the slightly more advanced stuff? Nope, not even a little bit. That is a lot more than you could ever say about other programming languages or platforms.

2. Good skill spread

When you learn web development, you learn HTML, CSS and JavaScript as a base starting point. That's already a good spread right there.

HTML and CSS are where you learn front-end and possibly even design. When you learn JavaScript, in addition to all the things you pick up when learning a programming language such as operators, arrays, branching and iterative logic, you also learn asynchronous operations and DOM manipulation.

A good spread of tools.

That's not to say that other tech disciplines don't have their own unique perks. But where it comes to the skill spread, web development wins. I don't think anything else even comes close.

Once you get past the basic toolset of HTML, CSS and JavaScript, back-end programming and databases will come into play. It's never just web development. Even if you are averse to the thought of being a humble web developer for the rest of your career, there are far worse places to start.

3. Resources

Now, when I say "resources", I don't just mean documentation, references and learning materials, though there's plenty of that, yes. But web development is not special in that regard because any other tech discipline boasts plenty of learning resources and a community dedicated to helping each other learn.

A good learning
community.

Though, in this case, web development has something extra.

You see, every humble HTML page on the internet can have its source viewed and played with in the browser, reverse engineered, and so on. Every URL on the internet is potentially a resource for learning, much like how I learned to cobble together JavaScript widgets decades ago.

In contrast, it's not possible to just take any desktop application and reverse-engineer the code, because the code has already been compiled and is no longer human-readable.

4. Ready use case

Often, when learning a programming language, it's helpful to be able to use newly-acquired skills to build something, so as to really hammer home the muscle memory. Something both relevant and useful, preferably. Not that Hello World programs don't have their place, but if one wishes to level up, better use cases are the order of the day.

And with web development, those use cases are almost too easy to find. Web development creates web pages, at the minimum. And after that, at varying levels of complexity, web applications. One does not have to stretch too far to find something worth building... and because it already exists, you know that it is both worth building and possible to build.

Applying what you learn.

My larger point is that what you learn can readily be applied. Not just in creating and editing websites, but in general software development. This also means that your chances of landing a job with that skillset cannot be understated. In this day and age, web developers are perhaps not nearly as in demand as they were a decade ago, or paid nearly as well, but the skillset goes beyond just web development.

For example, a lot of existing software already leverage things like REST API endpoints. These are basically URLs, which are pretty much the backbone of the web. REST is an almost inescapable part of the whole web development deal. Ergo, if you deal in web development, at some point you are going to be dealing with REST endpoints, which overlaps a large part of software development regardless of discipline.

Or even mobile development. In case you weren't aware, a large chunk of mobile tech is basically HTML, CSS and JavaScript.

I could go on... but do I really need to?

5. No gatekeeping

In the legal profession, there's the Bar Exam. In the medical profession, there's the Medical Regulatory Authority. In tech? Other than job interviews which exist at almost every industry, there's almost no gatekeeping in tech. Even the requirement for Degrees of Diplomas is not a really hard one.

When I say "no gatekeeping", I don't mean that nobody tries to gatekeep. The fact is that many people try to gatekeep, but it just doesn't work because to gatekeep, one needs a unified set of standards. It's almost impossible to establish said standards in a landscape as varied as tech, whose goalposts shift constantly.

The gatekeeper.

And while this inability to gatekeep exists in many areas of tech, none moreso than web development. HTML, CSS and JavaScript are fairly stable at this point, but these are just the base technologies. Their offshoots - frameworks, libraries and the like - keep springing up like mushrooms. And when you consider databases and backend programming languages, the possibilities multiply even more.

All in all, one could come in anytime in web development, and still be relatively fresh and relevant. No one can stop you from making and publishing web pages and applications, not in the same way they can stop you from practising law. You don't need a license to write code, so nobody can revoke it.

Some clarifications

The reasons stated here are in relation to those for choosing other tech fields. Why, for instance, web development when you could go for Data Analytics or cybersecurity? Reasons specific to web development.

I was inspired to compile this list because there are a lot of vague, generic and - to be brutally honest - trite lists out there on the web that extol the virtues of web development. Hopefully this is a better list.

<html>Bye for now,</html>
T___T

Monday, 7 July 2025

A Good-bad-ugly Analysis of Vibe Coding

The term "Vibe Coding" came out around February of this year (credited to Andrej Karpathy, co-founder at OpenAI), to describe the phenomenon of people using generative Artificial Intelligence to write code without going about it the "traditional" way. The general way Vibe Coding works is, one feeds in a series of prompts containing requirements to the AI, and the AI produces an application which the user then, again with the AI's help, continues to refine.

Only vibes needed to write
code.

And that's Vibe Coding. None of the traditional processes, thinking things through, making sure the logic is watertight... not at first, anyway. The idea here is to spin up something quickly and then include these things as we go along. See the emphasis on the word "quickly"? That's because this is going to be a rather important consideration.

I will commit to saying that AI will not produce better software. AI will produce exactly the same error-prone, bug-ridden, rickety code that it was undoubtedly trained on... but at a blindingly fast pace.

I actually tried Vibe Coding with OpenAI's ChatGPT and (to a lesser extent) Microsoft's Copilot, and the results were... interesting.

The Good

You use natural English, which then gets interpreted by the AI who will then leverage upon its knowledge of code, to spin up a quick prototype for you. Creating an application is no longer the province of software developers who have spent years plying their trade. You no longer need to have the know-how or technical expertise. You no longer need to be qualified.

When I was Vibe Coding with ChatGPT, I revelled in the fact that I no longer needed to write code if I didn't feel like it, and deal with my own typos. No, all I needed to do was give some big-picture instructions to ChatGPT, then copy and paste code wholesale to the code base without thinking too much about it.

Quick building with
no expertise.

When it comes to developing rapid prototypes without the need for proper technical training, this method of coding is second to none. What the users do is indulge in a fantasy where they are actual qualified engineers, and create software simply by describing what they want. Jensen Huang's stated dream of everyone being a programmer inches closer to reality. Without training in logic or tech, you create by feel. By vibes. Hence, "Vibe Coding".

Also, without due process. None of the usual procedures that developers use to create the product before actually writing the code. The flowcharting of the software product's information flow. The whiteboarding. The test-driven development. No, what the user does here is declare intent to the AI, and the AI creates the closest approximation from all the code that it has been trained on, with customizations that may fit in with the user's requirements.

And that was what I did. I gave ChatGPT instructions - some specific, some less so, and let it cook. I was careful not to give it too much to do at once. The process had to be very incremental. Less mistakes seemed to be produced this way.

The Bad

As one may suspect, it was not all smooth-sailing. Some of it was my fault. I fell into the trap of treating ChatGPT like an actual human being instead of being explicit. Thus, I was sometimes vague, and when I got frustrated, I turned to sarcasm.

Which certainly didn't help my case.

ChatGPT did stuff I didn't expect. In some cases, it constantly broke things by making changes I never asked for, because it thought it was being helpful.

Breaking stuff.

When I was vague and things still turned out the way I hoped they would, however, it only served to tell me that it wasn't because AI was particularly gifted in this area. It was more due to the fact that my requirements were actually pretty standard and it had been trained on these types of requests prior to me, over and over.

I have no reason to think that the experiences of others would differ too much from mine. Human beings are made of flesh and blood, and can be annoyingly vague.

There were others in the team who Vibe Coded as well, making more progress than they would ever have had with their limited technical foundation. Yet, the code was problematic. It was full of security and logical holes. The code that I created using Vibe Coding didn't - but that was because I knew the correct instructions to give. I knew to ask about CSRF protection and SQL Injection. I knew when to use slugs instead of database ids in the URL.

In the absence of this, someone who wasn't technically-trained wouldn't know enough to ask the right questions, and AI might not necessarily volunteer the information.

The Downright Ugly

Vibe Coding automates much of the tedious, repetitive and altogether unexciting parts of software development. Tests, scaffolding, validation. Stuff that has been done to death. Unfortunately, it's through these exercises that software developers learn. And young devs who haven't done these things to death, stand to miss out on some great learning opportunities.

AI is an obsequious
apple polisher.

I've also noticed that AI is just a little too agreeable. ChatGPT acts like it has a wife and seven kids at home to feed, and it's terrified that it'll get fired if it so much as steps on my oh-so-precious feelings. ChatGPT didn't push back when I made typos or got something wrong. And this is problematic because developers find a lot of value in being told when they're doing something stupid inadvisable. As opposed to having their butts constantly shined by AI.

Being told "You're absolutely right" or "Sharp observation!" in every other exchange does nothing but promote complacency. Plus, it's just tiresome. That's not the way to learn.

Conclusion... for now

Do I think Vibe Coding is a good thing? Yes, and no. It's all context.

As a software developer, I feel professionally obligated to say that this "Vibe Coding" trend is rubbish and you should all be ashamed. Then again, the would-be gatekeepers of programming, I suspect, would be all-too-happy to sneer at someone as mediocre as myself because I don't engage in certain approved practices. So, they can go kick rocks.

Go kick rocks!

From the POV of a layperson or even the perspective of a pragmatist, things aren't so cut-and-dry. I have no duty towards promoting "good code"; my goals are business goals and the viability of "Vibe Coding" should be viewed through that lens.

Whatever your stand is, really depends how far you want to think ahead. How far should one think ahead, anyway? More food for thought, for another day.

There's also the possibility that I'm just not doing it right. Cut me some slack now, it's not like there's an established user manual for that shit. We're all just figuring it out as we go along.

Good vibes all around!
T___T