Friday 28 September 2018

The Need For Foreign Tech Talent

Just a couple weeks ago, Ong Ye Kung, Education Minister of Singapore, gave an interview at Bloomberg. There, he said that Singapore would maintain little restriction on foreign labor for high-end jobs, while keeping a quota system for lower-skilled industries.

"Talent is very short everywhere in the world - AI talent, software programmers. We let them in because we require a critical mass for the sector to take off, while we continue to train Singaporeans for those jobs."


Predictably, the Internet was up in arms, mostly ranting about short-sighted governance and how foreigners are let in to steal our jobs, and vote for the ruling party to perpetuate their rule, etc. The problem I have with these people commenting is that most of them, I bet, haven't spent a single working day in my industry. And suddenly they're the experts on whose fault it is and what the Government should have done? Come on. In local parlance, bro, you not shy, ah? Sure, anyone can have an opinion. But if your opinion isn't formed by any relevant first-hand experience, maybe you should stick to what you do know, like the price of soy beans, or something.

Your Teochewness will now proceed to give you some perspective - from an actual software developer's point of view.

The programmer shortage is worldwide. The rapid and constant rise of technology has all but ensured that there are more programming jobs than there are programmers - even if you count in the really crap programmers! For this to be the Singapore Government's fault, they would have had to mastermind this global shortage somehow.

Now, let it be known that I'm no fan of Mr Ong. I don't exactly despise the man; I just don't have much of an impression of him other than The Mindef Bug Bounty Programme back in January, and an ultra-lame "Kway Teow Hot and Nice" election campaign back in 2011. That being said, despite not being a fan of his, I found nothing wrong with what he said, at all. Singapore does need tech talent. And we're not about to pull tech talent out of our asses just like that. These things take time to nurture. In the meantime, what do we do? Import, but only if we have to.

Welcome to Singapore!

You see, I'm not one of those nutters claiming that the Government hasn't done enough. No, my complaint is the exact opposite. I think the Government overdoes things. Back then when they first started to import foreign talent, they opened the gates wide. Let them in, in droves. Pretty much put off a large percentage of tech people, who left the industry to seek life elsewhere. Now, years later, they're starting to tighten things up rather too enthusiastically, and this comes in the form of just about slamming the gates shut. In my workplace, I'm the only Singaporean at my level surrounded by Indians, Burmese, Viets and Filipinos. When these developers are let go or leave the company, it's hard to find replacements. No foreign candidates apply because they've either had to leave the country, or they're happily employed elsewhere. No other Singaporeans have applied for the job because there are no available Singaporean candidates. That's how bad it is now.

Sure, we can grow this local tech talent organically. But again, let's not kid ourselves, this takes time.

Meanwhile, do bear in mind that the life of a software developer isn't for everyone. If you think growing home-grown tech talent is merely a matter of providing more subsidies and expanding facilities, boy, do I have a surprise for you. The Government can encourage people to take up courses and learn to code, sure, but it's a life of never-ending upgrades and constant adjustment. It's an industry that developers can spend ten years in, and yet somehow find themselves being less relevant than a wet-behind-the-ears kid fresh out from University.

Software development, and arguably all tech, is a profession where change is the only constant, and the notoriously conservative mindset of Singaporeans simply does not lend itself to this paradigm. Many people are accustomed to the age-old formula of - get a job, work hard, stay in the company long and become "senior staff", rinse and repeat till retirement. That corporate drone mindset is not quite how it works here. Developers are constantly on their toes because technology keeps evolving.

Could you code for a living?

Anyone can learn to code. But not everyone can handle doing it for a living. You can't force a naturally change-resistant personality to embrace the perpetual change that is part of software development. Skills can be taught. Mindset can't.

All Mr Ong is saying is that the Government intends to do what I feel they should have done ten years back - take a cautious approach to importing of foreign tech talent, continually adjust intake and monitor stringently for abuse of the system. And while doing so, nurture our own home-grown talent, ensuring that they have jobs upon graduation so that they can learn from these experts.

Easier said than done, of course. The Government probably already intended to do this back then; but to be fair, it was always going to be a difficult balance to strike. The ascension of the tech industry was not something that could have been accurately predicted. Singapore's not the only one to miss that particular boat.

Perhaps, this time round, things will be different. We'll see.

Happy recruiting,
T___T

Monday 24 September 2018

How I Became A Software Engineer (Part 3/3)

Another week passed, and I found myself interviewing at some logistics factory near my place. How near? Well, it was a matter of three bus stops from the MRT station. That's how convenient it was. I had a nice time. The interviewers were friendly and didn't waste my time with stupid questions. They'd seen my demos on my website and knew I could do the job, because some of it was more or less stuff they needed. But it was simply an opening in an IT Department in a non-tech firm. And they were using classic ASP. After everything I'd put myself through trying to level up, I had this niggling feeling I should be doing bigger things. Hell, I was only 39 then!

Then I interviewed at a startup in Ubi. God, not Ubi again?! After everything that had happened, it felt like the Tai Seng/McPherson/Paya Lebar stretch was full of stupid assholes. A dude could develop a serious phobia of the area. But I kind of knew the guy. He was on my Facebook Friends List, for unrelated reasons. He had seen me share my web tutorials and blogposts over the last couple years.

I met the boss at a food court, dressed up as usual, and was fucking flabbergasted when I saw that he was dressed in t-shirt, shorts and slippers. That aside, we bonded over some shop-talk, and I realized immediately that his setup operated at a higher level than I'd ever encountered. This dude was good. And he was looking to replace his front-end guy. I did some reading on MeteorJS, the framework his company was using, and found myself intrigued by the notion of fully-reactive interfaces.



All in all, things looked promising on both sides. I wouldn't mind working for either setup.

Choices

A few days after the interview at the logistics company, one of the interviewers asked me out for coffee and flat-out told me that his company were interested. The only catch was that the hiring process might take months, so if I got anything in the meantime, he advised me to go for it. I have to admit, after all the bullshit I went through the past week, that touch of honesty was a nice gesture.

A nice relaxed session.

Though, it didn't change the fact that the gig at the Logistics company probably wasn't going to help me level up. The pay would be decent, and it would be an easy job... but I would be operating far below what I felt was my current level. I mean, the stuff I code in my free time is all well and good. It's fun. Like all pastimes should be. But as a professional, I want to do something bigger.

Then the startup guy came back to me and told me his front-end guy was leaving. It was game on. He sent me a contract, and to my dismay, there it was - The Probationary Pay Gambit. I would need to take reduced pay (500 SGD less) for three months. What is it with small companies and this reduced pay shit? But because I didn't think this guy was deliberately trying to take advantage of me (he was just under the impression that this was how small companies were supposed to roll) I politely informed him that these terms were not acceptable, and he offered an alternative. I would intern at his company for two weeks - for no pay - after which I would be paid what the contract promised.

Working for free?! WTF?

Now, working for free sounds counter-intuitive, I know. But let's look at it from another perspective.

If I worked three months at reduced pay and somehow didn't make it through probation, that reduced figure would be my last-drawn pay unless I felt like explaining to my next interviewer what I'd been doing the past three months. Two weeks was easier to explain away. Two weeks can fly by just like that.

If I took the three-month probation deal, I would lose (500 x 3 = 1,500) SGD. If I took the two-week deal and got paid half my full wages at the end of the month, I would lose more than two thousand dollars. On paper, just in terms of money (1,500 versus 2,000 plus), the three-month deal sounded better... but when you factor in the time (three months versus two weeks), no, it really wasn't. I was prepared to lose out a little financially for potentially far greater gain (last drawn pay) and insurance against failure.

A fork in the road.

Plenty of people I know would have opted to save that few hundred bucks. Some guys simply aren't big-picture people, and that's OK. I like to think I've gained perspective with age, and the wisdom to make sensible decisions.

There's nothing deep or profound about my decision. It was just math. The way I live my life, that few hundred dollars wouldn't make much of a difference. But time... time is precious for everyone. It goes, and it doesn't come back.

Internship

My internship would only begin after the weekend. In the meantime, my soon-to-be boss put me on a learning contract, where I had to finish items on an On-boarding checklist. I wouldn't say I nailed it, but over the weekend I did the required reading from the You Don't Know JS series, JavaScript: The Good Parts by Douglas Crockford and some web tutorials videos. This was starting to look like a really good learning opportunity. I was being exposed to stuff I never even suspected existed. It was intimidating, yes... and also excited me beyond words.

I went in on Monday and got introduced to the guys, one of whom I would be replacing. It was a rush. We traded nerd jokes and comments on programming. Every day, I went in at 9 AM and left after 8 PM, sometimes 9. Once or twice, even 10. I'd be there on the weekends while my new boss put me through the paces. I wanted this.

And I'm a Software Engineer!

Two weeks later, I got my appointment letter, along with being assigned a brand-new Mac and two large monitors. The appointment letter said that I had been hired as a Front-end Software Engineer from that day forth. Yes, "software engineer" is mostly just a job title. I'm not daft enough to think I can stand toe-to-toe with real engineers who have tons more training and experience. But trying to live up to that label would be fun, and fulfilling.

Most importantly, I was surrounded by people who were smarter than me. People who understood tech. I wasn't the most technically proficient guy in the room anymore. And that feeling was superb. I understood, of course, that I had yet to live up to the term "software engineer". Right now, it was just a title. I was still very much a web developer. But with this group, there was a very real feeling that I could bridge that gap.

My setup, complete with
gorgeous wallpaper.

Did I mention the setup? As a front-end software engineer, I had access to four different screens - one for code, one for running output, a tablet screen for viewing output on mobile devices, and the laptop screen. That alone was a better setup than anything I ever had.

Epilogue

This was sadly not to last. The company ran into cash-flow problems in less than a year. But I had a really good time while I was in this company. And whatever I learned probably propelled me to my next gig.

What was the point of this three-part blogpost?

What's the point of anything, really? It was a good story. I've had to bounce back from setbacks before. And by necessity, I've had to come back stronger. That always makes for a good story.

Also, I thought it was a good illustration for how much preparation a techie needs in order to land a job. All the work I put in on my blog, website and code repositories really paid off.

Good (job) hunting,
T___T

Saturday 22 September 2018

How I Became A Software Engineer (Part 2/3)

So I had a month of gardening leave to burn, followed by Reservist duties. I wasted no time applying for jobs, specifically looking for openings near home, because I was pretty sick of the Tai Seng/MacPherson area after having spent more than three years in the area. However, a fair bit of these openings were for WordPress and Joomla!, of which, after having spent the last three years trying to level up, I wasn't so keen on. Also, some of these guys seemed to be under the impression that I, fresh off what could be called a "retrenchment exercise", was available cheap. The fire-sale mentality. Sure, I no longer needed a good excuse for leaving my last job, but now I had to fight off the opportunists.

One of these companies really took the cake. In fact, they deserve an entire blogpost detailing just how much they suck.

It started off at an interview at Ubi. Yes, still around the Tai Seng/MacPherson area, but it was my policy not to turn down opportunities and at least hear them out. Being near my place was only a nice-to-have. Then the red flags just kept coming.

Red Flag #1 - Punctuality

When I arrived at the address at the appointed time, no one was in other than one employee who seemed utterly clueless. She was probably one of the developers. No senior staff were around. OK, fine. Punctuality is important, but I could accept that something came up last-minute. Eventually, one of the senior guys came back from lunch, and sat me down for a makeshift interview.

Form-filling.

Red Flag #2 - Redundant form-filling

Apparently, he wasn't prepared. He made me fill up this badly-photocopied form asking for information that should have been available from my resume. I swear, what is it with interviewers and forms?! This was obviously a tactic to buy time while he figured out what to ask, and who he should kill for saddling him with a task he was woefully unsuited for. I say this because...

Red Flag #3 - Stupid Ratings Questions

... he hemmed, hawed, and out of sheer desperation, asked me how I rated myself, on a scale of 1 to 10, in Joomla! and various other technologies. I've made my thoughts on this mode of questioning abundantly clear, and won't repeat myself. Needless to say, I was not impressed.

But... (silver lining!) he was a fellow smoker. After a little break on the balcony and a bit of informal chatting, I was willing to accept that he was just one of the senior staff and not really trained to interview. After that, I met his Manager, the one I was actually supposed to interview with. They arranged for me to return the next day for a technical test. Now that was more up my alley.

It turned out that they were in the business of producing parallax websites, and their chief developer of said websites had left. They were looking for someone who could produce the same thing. As part of the test, they gave me a sample and told me to produce one just like it. This took several hours of me reverse-engineering the code, and finding out that the example they gave me did not work anyway. Finally, they returned and I showed them the results. My own product was lacking; but I did show them why the example they gave me, which happened to be the work of the previous guy, wasn't working. They seemed satisfied.

Back home, I made my own parallax website and used the results in the Easter web tutorial of 2017. Hey, you learn wherever, whenever.

Over the weekend, I received a WhatsApp message from the boss. He wanted me to come in on Monday to discuss my employment. Apparently, they'd been suitably impressed. Suddenly, all the red flags I had encountered during the initial interview didn't seem to matter as much. Boy, how wrong I was...

I arrived after the lunch break, as scheduled. They all seemed to be busy, so I waited. Meanwhile, Mom had sent me a text message asking if I'd be going to her place for dinner. It seemed reasonable to say yes - I mean, how long could a simple discussion take? The meeting started soon, and the stakeholders started to ask me what was the possibility of setting up a CMS to create parallax websites, the tech involved, and so on. I answered to the best of my ability, and they seemed somewhat satisfied. Still, nothing resembling an employment discussion. Fine, I was content to wait.

This better be worth it...

Red Flag #4 - No concept of time

One of the stakeholders received a call from a client to make modifications to the site. He asked for my help, and mentally, I shrugged. It was just text amendments, no biggie. I might as well do something while I waited for them to get their act together. Before I knew it, it was 6:30pm, and my appointment for dinner at Mom's place looked like it wasn't going to happen. I gritted my teeth and waited, but their lack of organization was setting me on edge. I wasn't even an employee and they had me working on stuff. And on top of that, they didn't even have the courtesy to at least make it interesting?!

Finally, around 8:30pm, I dropped whatever I was doing and told that stakeholder flat-out that if they wanted to hire me, they would have to stop dicking around and make me an offer, or I could just go home because I had another interview the next day. But either way, I was done waiting for the discussion to take place.

He looked panicked, then hurried off to have a word with the boss, who stopped me on my way out, for that long-awaited discussion. I should have told him to fuck off, but what the hell, I was already there. How much worse could it get?

Red Flag #5 - Bullshit

In the meeting room, the boss tried to get me to name a figure for my expected salary, but I told him he would have to make me an offer - if I liked it, we'd talk some more, and if I didn't, then we wouldn't need to talk anymore. I know that sounds arrogant, but I was tired, hungry and pissed off. One hell of a potent combination.

He started by going "how about we pay you this amount first, and after a three month probationary period, we - " I stopped him right there, and explained, keeping my voice nicely calm and even, that there was no way I would ever accept such an arrangement. The Probationary Pay Gambit? What kind of noob did he think he was talking to?

He tried to convince me by saying they were going through a transition period right now and couldn't pay me what they thought I was worth, but I had the potential to be a technical director in their company one day. The smell of bullshit was getting stronger by the minute. (I may not be what people describe as "humble", but I do have a realistic view of my abilities.) And then he went on with "- so, if you trust me..."


Stop. Talking.

Whoa, buddy. I do not trust you. I have no goddamn reason to trust you. We've known each other for all of three days and in that time, you've managed to give me a very poor impression. And right now, everything you're saying sounds like typical douchebag sales double-talk.

I blew him off by saying I would think about it, and left the office.

Zounds! These guys were fucking unbelievable. They had wasted my entire day. A week later, the boss called me and upped his offer by more than a thousand dollars. I suggested that he go fuck himself. (No, I didn't actually say that - what kind of unprofessional asshole do I look like? But the meaning was clear enough.) He could have trebled his offer and I wouldn't have touched that shit company with a ten-foot pole.

No, my search would have to continue. Luckily, I had a few more prospects lined up...

Epilogue

I looked for the company's web site a couple years later and they were nowhere to be found. Looks like I dodged a bullet there. Niiiiice.

Next

How I landed that next job.

Thursday 20 September 2018

How I Became A Software Engineer (Part 1/3)

Just over three years ago, my professional life swerved off-course. I recovered from it like a boss and went on to greater things. Mostly because I've had worse, but also because I prepared for this eventuality well in advance. Still makes for a great story, though... so read on!

I was in the company described here, where I applied my newly-won Bootstrap skills. Everything went peachy for the first year. I met deadlines, applied myself, and took a course in mobile app development. Off work, I even had time to work on this blog and play around with all the new stuff I was learning. All was well. It didn't matter that I was often the most technically-qualified person in the room, or that some of the my colleagues (including those in higher management, the horror!) could not figure out how to use their email, much less understand the complexity of a web developer's role. But I was getting paid more than I'd ever been in my life (at the time, anyway) and on one hell of a roll.

Gradually, though, the dissatisfaction crept in. Remember when I complained about people who treat their employees like kids and insist on micromanaging them? This company had it in spades. Worse, the people who wanted to micromanage me couldn't carry my shoes in a development role. My direct Manager got complaints all the time about my toilet breaks, smoke breaks, latecoming... and he brushed them all off. What a guy. If there was one person who made my job tolerable, it was him, the only other technical person in the company.

Pay cut!

Then the day came. My Manager warned me of an impending company-wide pay cut. In a WhatsApp chat group opened specially for management personnel in the company, the Towkay had shared an article that predicted an economic downturn later that year. And like every other fat cat cheap bastard (with a mansion and swimming pool) I had ever worked for, he was using it to justify the pay cut. Within another day, a company-wide memo was sent out. Those at managerial level would receive 30% pay cuts, while the others would have 10% pay cuts. There was much consternation. Every staff member was called in to a meeting with the COO and HR Manager, to sign a document of acknowledgement of the pay cut. I did a few calculations and reasoned that I could lose 10% without things getting too uncomfortable. Even though I had been drawing an unprecedented level of pay, I lived like a guy who still drew 2,000 SGD a month.

Pay cut!

There was only one problem. They wanted to cut my pay by 30% instead of 10%.

The General Manager and HR Manager explained this by saying that I was drawing a salary on par with, or higher, than that of some of my colleagues who were Managers. And that was even after my Manager argued on my behalf that I didn't have the same benefits of actual Managers in the company. The COO and HR Manager then started to bring up my performance at work, such as latecoming, smoke breaks, and so on... somehow conveniently leaving out all the deadlines I'd met. And the fact that I'd never missed a day of work due to illness. That was when I realized just how much they resented me for not conforming to their long-cherished - and severely outdated - notions of what a valuable employee needs to bring to the table. Incidentally, this is one of the reasons I refuse to work directly for laypeople anymore.

I shut them up with an upraised hand and refused to sign the document, and told them I'd sleep on it and give them an answer the next day. So I did give it some serious thought.

I could leave the company, of course. The Towkay was playing this douchebaggy game of Chicken, banking on people being too afraid to do just that due to the projected economic downturn, and was taking this opportunity to squeeze his employees. Just on principle alone, I would have left. However, my Reservist duty was coming up in a month, and no company was going to hire me with that looming over my head. The best I could hope for was hunt for a new job after performing my Reservist duty, and then quit.

Back to the pressing issue of the pay cut. What if I reduced my working hours or took a couple days unpaid leave every week? Then they could deduct even more of my pay, and I would have more free time to code, work on my projects and maybe even find a new job. This was a win-win proposal, or so I thought.

Surprise - they turned me down. Apparently the Towkay was very insistent on his employees taking less pay for the same hours put in. (Again, see why I refuse to work for guys like these?) Once again, I refused to sign the document, and told them I would be discussing the issue with my Manager. The COO and HR Manager told me I should feel free to tell them how much of a pay cut I would be comfortable with. I promised to think about it, and give them an answer by the end of the day.

Putting on that friendly caring face.

At the third meeting, I started to tell them 30% was too much of a cut, and they told me this wasn't a negotiation. They just wanted to talk in order to get a better understanding of the difficulties of each employee... basically they wanted to appear sympathetic but their ultimate aim was to make us sign the document anyway. This was getting more ridiculous by the minute. The COO started to plead and I had this horrible feeling she was going to cry any moment out of exasperation. Tired of this charade, I patted her on the hand like she was this pitiful old aunty and signed the document. They tried to feed me more crap about how the company was going through hard times and we should all pull together. I'd had enough bullshit for one day and left the room.

Taking a 30% pay cut is one thing. But now I saw that they had begrudged me every cent they paid me since the beginning... because in their minds, people below managerial level should not be earning that much money. And from there, my decision to leave was cemented. In all honesty, I had been planning my exit for a while. I had merely thought to delay it for a year because they had facilitated the enrollment for the tech course I had recently graduated from. And using those new credentials to get a new job immediately after graduation just seemed wrong. But with their latest antics, they'd waived whatever professional courtesy and goodwill I might have owed them. And I had just brought the projected day of my exit very much forward.

My preparations for this day had been fairly extensive, and had started way before this entire episode. They mostly involved boosting my online credentials. For starters, I had my blog. The writing was sometimes amateurishly profanity-laden (still is, actually) but I had some nice writings on current affairs in tech which I replicated on my website. Sure, there weren't any extraordinarily deep thoughts in there, but at least it would show any prospective employers that I kept myself updated, and I actually bothered to pen my thoughts. I also had a GitHub account where I had uploaded code repositories. Working demos were hosted on my website. Again, the code wasn't groundbreaking; in fact it was really basic stuff that I had written web tutorials for. But actually having a GitHub account with working and tested code in it, is a visible measure of effort that does not necessarily speak for the quality of a developer, but definitely speaks for his enthusiasm

It all sounds like overkill, but you don't get by on a resume alone. Not in tech. And for my planned exit, preparation was key. All that stuff in addition to monitoring the job market and updating the resume. Remember what I said about resigning in a fit of pique? Yep, that's for noobs. You want to leave, fine. You better plan the shit out of it.

Surviving the pay cut

But first, I had to survive on reduced pay for a couple months until Reservist, after which I would be free to be hired by another company. Now that I had a plan and a timeline, living on a tight budget seemed really trivial. I didn't cut the amount I regularly gave my mother (hell, I didn't even tell her. Didn't want her to worry. I'm a grown-ass man and I can handle my own damn problems); instead I tightened the belt in every other area - cigarettes, food, and so on.

Tightening the belt.

Coincidentally, my appetite seemed to have shrunk (along with my waistline) during that period, and now with having to watch the expenses very carefully, I must have dropped about 10 KG in the ensuing few months. Honestly, having to make my own food daily and count my cigarette usage seemed to have done me a world of good.

My other colleagues were coping in their own way. Some tendered their resignation. Some took to mysteriously falling ill every other day and maxing out their MCs. Some just lengthened the duration and frequency of their breaks. The latter two cases were a source of consternation for management - apparently they'd been having this fantasy that those who chose to stay on would simply let themselves be squeezed.

Me? I didn't have time for any of that shit. Too busy planning my exit.

Company collapse

The day for Reservist drew near. I stepped up my efforts and prepared to put my plan into action. Everything seemed in place; the only thing I worried about was explaining why I had left this last job. Saying I had left due to a company-wide pay cut might give prospective employers the idea that I was open to exploitation.

Then the CEO dropped another bombshell. I was summoned into the meeting room, where the CEO, COO, HR Manager and my own Manager were waiting. I wondered what all this was about - what would warrant such a setup? I did not have long to find out. The CEO informed me, looking quite chagrined and regretful, that he had no choice but to let go an entire department (which included me, apparently) due to a botched client contract that rendered half the company obsolete.

Upset? Heck, no.

Were they expecting me to feel upset? It certainly looked that way. All of them were wearing expressions that suggested they were attending a funeral service. Me? Heck no, I was elated. Being let go due to the company crashing and burning was a hell of a lot easier to explain at my next interview, than a resignation. One of my biggest problems, solved right there. Plus, there was paid gardening leave. Things looked pretty sweet right there. I think it's safe to say I was the only dude smiling in the room. I was patting the CEO on the arm and going "Why the long face? Cheer up, life goes on!" Though I think the people most unhappy with this development were those who had already given their notice and weren't entitled to gardening leave.

So yes, Your Teochewness was free, and things had worked out even better than I'd expected.

What's this company?

I have a policy regarding speaking ill of my ex-colleagues and former places of employment. If I'm going to do that at all, they won't be mentioned by name. No, I'm not afraid of a lawsuit. I just have certain lines that can't be crossed.

Epilogue

A few months after I left, the company ceased operations. What a surprise.

Next

That's only the beginning. The trials that followed were pretty aggravating!

Saturday 15 September 2018

Web Tutorial: Nike Meme Generator

In 2016, quarterback Colin Kaepernick made waves when he opted to take a knee while the United States national anthem was being played before the start of NFL games. There were some who felt this was disrespectful, though, coming from a country where you can literally wear the flag next to your crotch, I'm really not seeing the problem.

Others saw it as a brave protest against racial injustice. I may be overly cynical here, but all I saw was an aging athlete trying his darnedest to stay relevant. Still... good-looking guy. Great shoulders. Build to die for. And the dramatic shots of him bravely enduring the jeers and abuse to "stand up for his beliefs" sure didn't hurt the narrative.

Fast forward to 2018, and Kaepernick has been made the new face of Nike, ostensibly because they were interested in his message. Methinks they were a wee bit more interested in milking the controversy surrounding Kaepernick and turning it into cash, but anyways. The first ad to be displayed was this.


Conservatives started losing their shit, but I'm not gonna go into that black hole of inanity. Suffice to say, a whole new wave of spoofed memes have been made off this. Brilliant, I say. Brilliant!



Therefore, today, I'll walk you through how to make your very own Nike Meme Generator!

What you'll need

This will be done in PHP (cue the groans from purist snobs. Bite me, dipshits.) and you'll need an Apache server to run your code. First, create a folder called uploads and put this picture of Kaepernick in there. It'll be called, quite appropriately, colinkaepernick.jpg.



Then create index.php in the main folder. And place this tiny white Nike swoosh in the main folder as well, and call it... nikelogo.png? It has a transparent background, and we're going to need that transparency.


You may want to refer to a previous web tutorial, The Asynchronous File Upload, also done in PHP. We'll be copying a fair bit of code from the repository.

Let's Begin!

This is the HTML we start with.
<!DOCTYPE html>
<html>
    <head>
        <title>Nike Meme Generator</title>

        <style>

        </style>
    </head>

    <body>

    </body>
</html>


In the body, we have three divs, ids pnlMessage, formContainer and memeContainer. pnlMessage is meant to provide user feedback (sucess or failure) while formContainer will contain the controls. memeContainer will contain the resulting meme!
<!DOCTYPE html>
<html>
    <head>
        <title>Nike Meme Generator</title>

        <style>

        </style>
    </head>

    <body>
                <div id="pnlMessage"></div>

                <div id="formContainer">

                </div>

                <div id="memeContainer">

                </div>
    </body>
</html>


Got all that? Good. Now here's some styling.

pnlMessage will take up the top. Its text will be red, it will take up the entire screen's width, and have a height of 50 pixels.

formContainer will have its width, but not height, specified. I've given it and memeContainer widths, paddings and margins, and the float property of left.

Not to mention a grey outline for all three divs so you can visualize what we're doing here.

memeContainer's width and height will be equal to each other, because it's square.
<!DOCTYPE html>
<html>
    <head>
        <title>Nike Meme Generator</title>

        <style>
            #pnlMessage
            {
                width: 100%;
                height: 50px;
                color: #FF0000;
                outline: 1px solid #DDDDDD;
            }

            #formContainer
            {
                width: 400px;
                padding: 5px;
                margin: 5px;
                float: left;
                outline: 1px solid #DDDDDD;
            }

            #memeContainer
            {
                width: 500px;
                height: 500px;
                padding: 5px;
                margin: 5px;
                float: left;   
                outline: 1px solid #DDDDDD;
            }
        </style>
    </head>

    <body>
                <div id="pnlMessage"></div>

                <div id="formContainer">

                </div>

                <div id="memeContainer">

                </div>
    </body>
</html>


Looking right? Now you have a good idea where everything is going to be.


Now do this so that only the outline remains for memeContainer. The other divs don't need it.
            #pnlMessage
            {
                width: 100%;
                height: 50px;
                color: #FF0000;
                outline: 0px solid #DDDDDD;
            }

            #formContainer
            {
                width: 400px;
                padding: 5px;
                margin: 5px;
                float: left;
                outline: 0px solid #DDDDDD;
            }

            #memeContainer
            {
                width: 500px;
                height: 500px;
                padding: 5px;
                margin: 5px;
                float: left;   
                outline: 1px solid #DDDDDD;
            }


Now, within formContainer, we'll have a form tag. It points to this page itself, and the enctype attribute needs to be set to "multipart/form-data" because we're using the form to upload an image file. We have three text boxes (for the first and second line of the meme, and the Nike slogan), a file input object, and a submit button. And of course, their respective labels.

As per The Asynchronous File Upload web tutorial, there's also a hidden field, id hidUploadSize, that will be used to restrict the size of the file being uploaded. Not absolutely necessary, but I'm paranoid like that.
        <div id="formContainer">          
            <form id="frmUpload" name="frmUpload" action="" method="POST" enctype="multipart/form-data">
                <label for="flUpload">File</label>
                <input type="file" name="flUpload" id="flUpload">
                <input type="hidden" name="hidUploadSize" id="hidUploadSize" value="50000000">
                <br /><br />
                   <label for="txtLine1">Line 1</label>
                <input name="txtLine1" id="txtLine1" maxlength="50" value="" />
                <br /><br />
                   <label for="txtLine2">Line 2</label>
                <input name="txtLine2" id="txtLine2" maxlength="50" value="" />
                <br /><br />
                <label for="txtSlogan">Slogan</label>
               <input name="txtSlogan" id="txtSlogan" maxlength="20" value="" />
               <br /><br />
               <input type="submit" name="btSubmit" id="btSubmit" value="Create your Meme!">
           </form>
        </div>


This is not a very pretty form, but it's functional and that's all we really need.


Now for some PHP!

At the top of the file, before the doctype declaration, declare a PHP block, then the following variables as defaults. So the  default meme will use colinkaepernick.jpg and the default text will be "Believe in something. Even if it means sacrificing everything.", followed by the slogan "Just Do It.". By default, strmessage will be an empty string.
<?php
$filecode = "colinkaepernick";
$filetype = "jpg";
$line1 = "Believe in something.";
$line2 = "Even if it means sacrificing everything.";
$slogan = "Just Do It.";

$strmessage="";
?>

<!DOCTYPE html>


Next, we use an If block to check if a form has been submitted. If so, replace the current values of line1, line2 and slogan with the values POSTed from the form.
$filecode = "colinkaepernick";
$filetype = "jpg";
$line1 = "Believe in something.";
$line2 = "Even if it means sacrificing everything.";
$slogan = "Just Do It.";

$strmessage="";

if (isset($_POST["btSubmit"]))
{
    $line1 = $_POST["txtLine1"];
    $line2 = $_POST["txtLine2"];
    $slogan = $_POST["txtSlogan"];   
}


Then check if there's been a file upload. If not, set an error message to strmessage. line1, line2 and slogan are optional.
if (isset($_POST["btSubmit"]))
{
    $line1 = $_POST["txtLine1"];
    $line2 = $_POST["txtLine2"];
    $slogan = $_POST["txtSlogan"];

    if (basename($_FILES["flUpload"]["name"]) != "")
    {

    }
    else
    {
        $strmessage="No file selected.";
    }   
}


If there has been an upload, verify that the size of the upload doesn't exceed the size you specified in hidUploadSize. I don't want to repeat myself; all these steps can be found in the earlier web tutorial.
if (isset($_POST["btSubmit"]))
{
    $line1 = $_POST["txtLine1"];
    $line2 = $_POST["txtLine2"];
    $slogan = $_POST["txtSlogan"];

    if (basename($_FILES["flUpload"]["name"]) != "")
    {
        $uploadsize = intval($_POST["hidUploadSize"]);
        $filetype = pathinfo($_FILES["flUpload"]["name"],PATHINFO_EXTENSION);
        $filetype = strtolower($filetype);

        if ($_FILES["flUpload"]["size"] > $uploadsize)
        {
                $strmessage = "Error was encountered while uploading file. File cannot exceed " . ($uploadsize/1000) . "kb";
        }
        else
        {

        }
    }
    else
    {
        $strmessage="No file selected.";
    }   
}


The next step, however, is not in the previous web tutorial. Here, we check if the file uploaded is a valid image file. If not, we set the value of strmessage to an error message.

How, exactly, do we check if it's a valid image file? We certainly can't simply go by the extension. That's one of the easiest things to fake. So what we do, is use the getimagesize() function in PHP. Pass in the temporary filepath from the $_FILES["flUpload"] array. If the file is an image, the function should return you an array. Thus, if we use the is_array() function on the result and it comes back false, the uploaded file is not an image file.
if (isset($_POST["btSubmit"]))
{
    $line1 = $_POST["txtLine1"];
    $line2 = $_POST["txtLine2"];
    $slogan = $_POST["txtSlogan"];

    if (basename($_FILES["flUpload"]["name"]) != "")
    {
        $uploadsize = intval($_POST["hidUploadSize"]);
        $filetype = pathinfo($_FILES["flUpload"]["name"],PATHINFO_EXTENSION);
        $filetype = strtolower($filetype);

        if ($_FILES["flUpload"]["size"] > $uploadsize)
        {
                $strmessage = "Error was encountered while uploading file. File cannot exceed " . ($uploadsize/1000) . "kb";
        }
        else
        {
            if (!is_array(getimagesize($_FILES["flUpload"]["tmp_name"])))
            {
                $strmessage = "File type invalid";
            }
            else
            {
   
            }
        }
    }
    else
    {
        $strmessage="No file selected.";
    }   
}


And at this point, you upload the file.
if (isset($_POST["btSubmit"]))
{
    $line1 = $_POST["txtLine1"];
    $line2 = $_POST["txtLine2"];
    $slogan = $_POST["txtSlogan"];

    if (basename($_FILES["flUpload"]["name"]) != "")
    {
        $uploadsize = intval($_POST["hidUploadSize"]);
        $filetype = pathinfo($_FILES["flUpload"]["name"],PATHINFO_EXTENSION);
        $filetype = strtolower($filetype);

        if ($_FILES["flUpload"]["size"] > $uploadsize)
        {
                $strmessage = "Error was encountered while uploading file. File cannot exceed " . ($uploadsize/1000) . "kb";
        }
        else
        {
            if (!is_array(getimagesize($_FILES["flUpload"]["tmp_name"])))
            {
                $strmessage = "File type invalid";
            }
            else
            {
                $filecode=strtotime("now").rand();
               
                if (move_uploaded_file($_FILES["flUpload"]["tmp_name"], "uploads/" . $filecode . "." . $filetype))
                {
                       $strmessage = "File uploaded.";
                }
                else
                {
                       $strmessage = "Error was encountered while uploading file.";
                }
            }
        }
    }
    else
    {
        $strmessage="No file selected.";
    }   
}


Alter the HTML to show the variable strmessage.
<div id="pnlMessage"><?php echo $strmessage; ?></div>


Try uploading invalid files, or files that are too big. In this example, I tried to upload a PDF.


And after that, let's add the default variables into the form. Just for the users' convenience.
            <form id="frmUpload" name="frmUpload" action="" method="POST" enctype="multipart/form-data">
                <label for="flUpload">File</label>
                <input type="file" name="flUpload" id="flUpload">
                <input type="hidden" name="hidUploadSize" id="hidUploadSize" value="50000000">
                <br /><br />
                   <label for="txtLine1">Line 1</label>
                <input name="txtLine1" id="txtLine1" maxlength="50" value="<?php echo $line1; ?>" />
                <br /><br />
                   <label for="txtLine2">Line 2</label>
                <input name="txtLine2" id="txtLine2" maxlength="50" value="<?php echo $line2; ?>" />
                <br /><br />
                <label for="txtSlogan">Slogan</label>
               <input name="txtSlogan" id="txtSlogan" maxlength="20" value="<?php echo $slogan; ?>" />
               <br /><br />
               <input type="submit" name="btSubmit" id="btSubmit" value="Create your Meme!">
           </form>




Let's modify the CSS a bit. The memeContainer div needs to have a background image specified, and in this case, we use colinkaepernick.jpg as a default (that's what filecode and filetype are set to). Make sure it covers the entire square by setting background-size to cover. Do whatever you want with the font, but I'd recommend white. The filter property is important here. It shows images in greyscale.
            #memeContainer
            {
                width: 500px;
                height: 500px;
                padding: 5px;
                margin: 5px;
                float: left;
                outline: 1px solid #DDDDDD;
                background: url(<?php echo "uploads/" . $filecode . "." . $filetype; ?>) center center no-repeat;
                background-size: cover;
                font-family: georgia;
                color: #FFFFFF;
                font-size: 25px;
                -webkit-filter: grayscale(100%);
                filter: grayscale(100%);
                text-align: center;
            }


Oooh.


Now, for the text within the meme, I can't be arsed to create a new CSS class for this, so I'll just go with two paragraph tags, one of which is set in the middle of the box, and the other at the bottom. The first paragraph will display line1 and line2. The second paragraph will display the Nike swoosh and the string slogan.
        <div id="memeContainer">
            <p style="margin-top:50%"><?php echo $line1;?><br /><?php echo $line2;?></p>
            <p style="margin-top:30%"><img src="nikelogo.png"> <?php echo $slogan;?></p>
        </div>


There's your meme! Well, the default anyway.


Let's try something here... well now we have a Serena Williams meme! If you're wondering if I'm mocking her for her recent display at the US Open... yes, I really fucking am.


One last thing...

Let's add a bit of CSS so we can print. formContainer and pnlMessage will be totally invisible, while memeContainer will have the float property set to none, and then aligned center of the screen.
            #memeContainer
            {
                width: 500px;
                height: 500px;
                padding: 5px;
                margin: 5px;
                float: left;
                outline: 1px solid #DDDDDD;
                background: url(<?php echo "uploads/" . $filecode . "." . $filetype; ?>) center center no-repeat;
                background-size: cover;
                font-family: georgia;
                color: #FFFFFF;
                font-size: 25px;
                -webkit-filter: grayscale(100%);
                filter: grayscale(100%);
                text-align: center;
            }

            @media print
            {
                #formContainer, #pnlMessage
                {
                    display: none;
                }

                #memeContainer
                {
                    margin: 10% auto 0 auto;
                    float: none;
                }
            }


Do a Ctrl-P. Beautiful, isn't it.


Last words...

Well, I certainly enjoyed myself. Remember to sanitize your inputs (I didn't do it here because it's out of scope) to prevent a nasty XSS attack. And... have fun. Go crazy.

#justdoit
T___T

Tuesday 11 September 2018

Web Tutorial: The FizzBuzz Solution

Following my piece on FizzBuzz last month, I thought it would be fun (see what my idea of fun is? Man, I'm such a geek) to walk you through the painfully simple process of a FizzBuzz program. Of course, the program is simple enough - what's really important is understanding the process.

To make it even more fun, I'm going to do it in Ruby. Because... well, why not?

First, at the risk of sounding repetitive, let's look at the program specs again.
Write a program that prints the numbers from 1 to 100. But for multiple of three print "Fizz" instead of the number and for the multiples of five print "Buzz". For numbers which are multiples of both three and five print "FizzBuzz".


So, it sounds like this requires some kind of For loop. But let's keep this small, say, 20 instead of 100.
for i in 1..20

end


By default, we print out the number.
for i in 1..20
    puts i
end


And here we are.


Now, this is hardly optimal. We are going to have some conditionals in between. So since the default is printing out the number, let's first create a variable, output, and assign the value of i to it. Then print out output. This will still give you the same output, but your code is more extensible now.
for i in 1..20
    output = i

    puts output
end


Here's the first condition. If the Modulus of i/3 is 0, which means that i is divisible by 3, the value of output is "Fizz".
for i in 1..20
    output = i

    if i % 3 == 0
        output = "Fizz"
    end

    puts output
end


Yep. Seems to be working!


And if i is divisible by 5, the value of output is "Buzz".
for i in 1..20
    output = i

    if i % 3 == 0
        output = "Fizz"
    end

    if i % 5 == 0
        output = "Buzz"
    end

    puts output
end


Getting closer...


Now for the grand slam! If i is divisible by both 3 and 5, the value of output is "FizzBuzz".
for i in 1..20
    output = i
   
    if i % 3 == 0
        output = "Fizz"
    end

    if i % 5 == 0
        output = "Buzz"
    end

    if (i % 3 == 0 and i % 5 == 0)
        output = "FizzBuzz"
    end

    puts output
end


Great. It works. Just one more thing...


Make the program loop 100 times instead of 20.
for i in 1..100
    output = i
   
    if i % 3 == 0
        output = "Fizz"
    end

    if i % 5 == 0
        output = "Buzz"
    end

    if (i % 3 == 0 and i % 5 == 0)
        output = "FizzBuzz"
    end

    puts output
end


Mission accomplished!


Final notes

Yes, it really was a simple piece of work. And does nothing useful at all.

As mentioned, FizzBuzz is really just a way to ensure that a programming candidate understands how to use loops, conditionals and the Modulus operator. We did it in Ruby, but it really could have been done in any programming language.

puts "Bye!"
T___T