Author: Justin Lee

  • Are Network Load Balancers Faster? A story on Engineering decisions.

    Are Network Load Balancers Faster? A story on Engineering decisions.

    I was working on a project and an Engineer approached me after going through the AWS environment. He made a recommendation to switch from an Application Load Balancer (ALB) to a Network Load Balancer (NLB), and his reason was that the application may potentially receive high traffic and that the NLB has better performance.

    Well, he is not wrong because AWS’s documentation states“If extreme performance and static IP is needed… we recommend you use a Network Load Balancer.”

    However, the statement from AWS concerns only the performance capabilities of the load balancer — it doesn’t mean that your application as a whole would have better performance.

    Whatttttt??? I’m talking rubbish, right?

    I used to work with a telco to build and maintain HTTP Load Balancers back in 2009. We were load balancing at its peak around 20 Gbps of HTTP traffic to a web cache farm sitting in the core of the telco’s network. Web caches were really important for user experience because most of the web content that Singapore users consumed was overseas.

    Serving up 20 Gbps of web traffic was a huge feat during that time — most PC still had 100 Mbps LAN, and we didn’t even have fiber broadband in Singapore yet. We had around 40 web cache servers, each only capable of handling around 500–600 Mbps of load. The bottleneck on the cache server was disk I/O and CPU.

    The optimizations that HTTP LBs do became very important. Good HTTP LBs advertise all sorts of fancy features for a reason (because people need them), but the most important bit is that it takes work away from the backend servers — the LBs we used back then (Citrix NetScaler) will multiplex multiple HTTP requests across a single TCP connection. This made a HUGE difference to the web cache server performance. Without this feature, each web cache can barely handle 100–200Mbps of load because under millions of requests TCP connections are being set up and torn down. If you know how HTTP servers work, you will know that every TCP connection is a new thread which is an expensive operation.

    A few years later, I was once again dealing with LBs for a US tech startup. At the peak, they were getting millions of API requests and their servers were struggling. I replaced traditional NLBs with ALBs and it reduced the load of the backend servers by 20–30%.

    In most cases, backend servers are already busy doing what it needs to do — business logic, database access, etc. What you’d want is to have the LB offload any extra header processing, routing rules, redirection/filtering, SSL, etc. so your servers don’t have to. Another feature of an ALB is its ability to use more intelligent load distribution algorithms based on application-aware parameters such as HTTP headers, which can be very important with HTTP applications.

    The Engineer made an assumption that an NLB will yield better performance — but we didn’t have data, and didn’t have an actual performance issue.

    As Engineers, we need to know how to do work with meaningful impact and outcomes and avoid trying to prematurely optimize based on assumptions.

  • What are the kids thinking? A peek into the future of the Metaverse

    What are the kids thinking? A peek into the future of the Metaverse

    Earlier today my eldest son (7 years old) came up to me and asked me to help him buy Robux, the in-game credits for Roblox. He wasn’t asking me to pay for it — instead, he handed me $10 from his angbao savings and wanted to spend it on dressing up his Roblox avatar.

    Obviously I said: “No.”

    Later in the evening I was working on my old IBM laptop that is now around 16 years old. It runs Linux and I was using it to wipe some old hard drives I intended to dispose. While the slow wipe was running, I was bored and decided to play a retro game on my retro laptop: DOOM.

    In the game, this help screen appears when you hit F1:

    Screenshot of the Help screen in Doom (1993)

    What caught my attention was that you could buy Doom for $40 (USD) including Shipping & Handling. $40 was quite a bit of money back in those days, but it was not just a game – Doom was one heck of an engineering feat for the price.

    A little history — you can skip this part if you aren’t interested: Doom was developed back in 1993. When Doom was built, the game engine itself was entirely new and it was the first time the world has ever seen such real-time (pseudo-)3D graphics in a home computer. This was around the same time Intel launched the Pentium processor after its success with the 486. Let me repeat, in case it was not obvious: 66MHz was the fastest desktop CPU you could get at that time; your microwave oven probably has a faster CPU today. Games these days rely heavily on layers of technologies built over the years — hardware GPUs, 3D libraries/APIs (like DirectX/OpenGL), and game engines (like Unity). There were no such things back in 1993. Graphics in Doom is purely CPU-rendered.

    But if I went to my parents back in 1993 and asked for $40 to buy the game, my parents would have gone:

    “SIAO AH?”

    This was what got me thinking: what was my son’s motivation behind paying for fancy clothes on his virtual character?

    My generation today have accepted that it is normal to buy games. The transaction rewards the buyer with entertainment (playing the game), and the seller for their efforts (creating the game.)

    30 years ago, my parents would have been paying for something that their parents (i.e. my grandparents) would have thought were simply a waste of money. 30 years from now, our kids will be paying for something that we thought was nuts today.

    I was initially skeptical, but Mark Zukerberg may be up to something with this whole Metaverse thing. The technology is probably a bit too early for its time. Something must first exist to bridge the gap, and I think it might be in the form of an immersive, social-gaming app.

    Another thing: There’s some misconception that the Metaverse is/needs Virtual Reality (VR); VR is one of many technologies that will enable us to live in the Metaverse, but it’s not the only technology. It is likely true that huge advancements in Virtual Reality (VR) or Augmented Reality (AR) technologies will enable more immersive and engaging metaverse experiences. The VR headsets we have today are akin to the PCs in the 1990s running pixelated 3D games: It looks crappy and uncomfortable to wear, and is sort of at the edge of getting better with graphics looking quite decent, but in due course the hardware size, performance and quality will likely catch up.

    The fact though, is that some aspects of this Metaverse is already here. Think about where people are spending more time and money.

    What does the future look like then?

    Think about a world where the digital resources becomes even more convenient and easy to access; think about the walls of your homes simply made of large digital screens where you could do anything you wanted – meet a friend, go to work, pull up a photo; think about getting into a train but being in a virtual world where you could still talk to your kids at home… or on Mars?

  • Open Source Primary and Secondary Educational Resources

    Open Source Primary and Secondary Educational Resources

    Can Primary, or even Secondary education materials in Singapore be made Open Source so that educators or even members of the public contribute and consume freely? Can and should such education materials be made free and open for all?

    What triggered me was the need to sign up for an account before I could even browse the booklist at an appointed book store for my child’s Primary school. After glancing through the list of books, it’s clear that only a few handful of publishers monopolize this entire market.

    I recall having gone through much of my youth depending on many external/supplementary resources including the famous “10-year series” as well as various other exercise books, notes, etc.

    When I went to Polytechnic where I was given a choice of my own, I did not buy a single textbook after my first year and instead relied on lecture notes (which were annoyingly only for purchase at the school’s printer) and additional material either on the Internet or the library for most of my studies.

    I also recall books getting stolen, because people who didn’t bring them didn’t want to get punished, so the stole others’. I also recall ripping apart super thick books by chapter so we would carry less weight around to school, and then getting scolded for it because defacing a textbook was an offence. (For the youngsters who don’t understand: back in those days we carry every single book to school every day – there were no lockers and we can’t leave them under our desks. The idea was that if we left the books in school, we don’t study them at home.)

    Long story short, my personal experience with textbooks hasn’t been great; if textbooks were perfect, our teachers wouldn’t need to come up with additional materials and creative ways to teach.

    Imagine a world where we could opt for books in print or PDF. Imagine a world where we could just print only the chapters we wanted. Imagine a world where if your child saw a typo error could just submit an edit back, and maybe even get recognized for it. Imagine a world where if we needed to see a newer or older version of a book we could simply download a different PDF, or look at a specific git tag? Imagine a world where underprivileged families wouldn’t have to get sponsorship to buy textbooks, but instead go and print a chapter they need at a community center?

    I’m sure every year thousands of educators create content that would have made our materials much better and more up-to-date if it were contributed back to a central, open, textbook. Our education ministry MOE can then act as the maintainer of the project – to curate and validate content before accepting it mainstream.

    Question is, where and how to start? Can our education ministry drive this? Can we see in 5, 10 years that Singapore has built such a strong Open Source resource that we have other countries use and also contribute back?

    Further reading: https://en.wikipedia.org/wiki/Open_educational_resources

  • Ten years of memories

    Ten years of memories

    I resigned from my previous job and joined a new one in 2020 – just right before the whole COVID-19 pandemic started.

    The “previous job” wasn’t just a job. It was a job I had for almost ten years since 2010. Ten years in the Tech industry today is quite unheard of – but I think many people do not stay long enough these days to see through the gravity of decisions they made and clean up the sh*t along the way.

    I was (and still am today) a Director of the business entity in Singapore. Since I am (still) a Director, I didn’t have to vacate my desk like a normal resigning employee would; I had intended to do so over several weeks or months, but COVID struck, and then weeks became months, months became years.

    It’s now September 2021 – almost two years from when I had resigned from my “previous job” and all my stuff are still in the office. Ten years of stuff to be precise. The office is undergoing a refresh now, so I decided it is really time to vacate my luxurious corner seat for those who deserve it, but as I start clearing out my desk I can’t help but feel very emotional…

    Ten years didn’t go by quickly, yet it felt like it was yesterday. I left a job as a Systems Engineer working shifts and camping in telco data centers to start the Singapore business operations for a small US-based boutique web hosting company. It was a bump in both my job responsibilities and salary.

    I have made the office my second home. As I pack my cubicle, every piece brings memories. Regretfully, I did not manage to take a photo of my entire desk area before I started clearing it out.

    Family

    There’s a bronze horse and another wooden pen holder with horse carvings – an item my dad handed down to me when I first “started” the business here. It is to wish me success: “马到成功” in Chinese. It reminds me that my family was behind me in all my endeavors.

    A photo of my wife and eldest son sits on a photo frame. I still recall when my son was around 1.5 years old I had to bring him to the office because the school was closed and I carried him in my arms while I worked at the office. It reminds me that sometimes you can’t separate work and family and it will always be a challenge to manage them both.

    My son sleeping on my lap while I’m at my desk, 11 October 2016

    Change

    I have a few R/C models on display and they’ve been sitting there for as long as I could remember. I had hoped to eventually pick up the hobby again but never did, and they remain as display items ever since. It reminds me that our priorities in life change and that change is the only constant.

    Old toys on display

    Learning

    I moved a bunch of books from my shelf to the common area bookshelf. Some of these books are from my tertiary education days; some on advanced topics that were not part of the regular school curriculum. There were also books bought during the course of work. However, most of these old books are no longer useful as they are outdated. It reminds me that learning never stops.

    I found a git cheat sheet hiding in a corner. I had printed it when I was first learning git, and had intended to use it but never did because Googling was just faster and easier. It reminds me that sometimes learning is not always a straight path.

    See the Git Cheat Sheet?

    I look at the 3D printer in the corner of my cubicle. I had purchased it on impulse as a birthday gift for myself in 2018. In fact, I was so busy during the period that I did not assemble it until a month later and it took me many attempts before I was able to print something decent. I have since used it to print many items for myself and even friends and colleagues to make replacement parts for real things, including car parts. It reminds me that sometimes you can learn new things as long as you try, and you’ll not know the real value of what was learned until later.

    Creality Ender 3 – covered in plastic to keep our the dust

    Health

    I pull out a booklet containing healthcare insurance policies that we have purchased only recently. Over the years I have paid out-of-pocket for several medical procedures, and do not want that to happen to any employees and have pushed to have this in place. It also reminds me that health is still the most important.

    People

    I look at all the stuff around my cubicle – gifts, items from events, business cards, etc. It reminds me of the number of people with whom I have crossed paths.

    I pull out a file containing the CV of every candidate I have interviewed and made an offer to over the years – some still current employees.

    These remind me that at the end of the day, we do not work alone.

    I look around the office and see the cubicles and desks of everyone. A company that was just two persons grown to a size of 7 and is profitable – not some VC-funded thing – reminds me of the constant struggles during the early phases as we tried to turn a profit.

    I am proud of the team, and my departure is probably timely because the situation makes the people. COVID-19 is a trying time, but in trying times lie opportunities.

    Off to my next adventure, and I hope to write a similar entry at the end of it.

  • Why Unit of Work is an anti-pattern but not Repository

    Why Unit of Work is an anti-pattern but not Repository

    Is Repository and Unit of Work (UoW) an anti-pattern? In the .NET/C# world, it is often said that Repository/UoW is an anti-pattern and one should probably use Entity Framework Core directly.

    But if it is an anti-pattern, why are people still using it? Even Mosh Hamedani – a respected YouTube coding trainer whom I follow – wrote about common mistakes when applying this pattern. Surely it must be popular.

    Purpose of the Repository/UoW Pattern

    Let’s talk about why this design pattern exists. It is actually a type of Adapter pattern and the primary use case is for separation of concerns between domain (or “business logic”) and infrastructure (or “data access”).

    The other motivation for using this pattern is to allow mocking repositories for unit testing.

    Must Repository go together with Unit of Work?

    It is also very common to see the Repository pattern used together with the Unit of Work (UoW) pattern. The UoW pattern is added to manage transactions – a feature of relational databases to perform ACID operations.

    However, there is increasing tolerance for eventual consistency and less atomicity; fully ACID operations are starting to matter less these days as software adopts the microservice architecture.

    So unless you are a bank, or dealing with critical financial data, maybe building your software with transactions management – a feature that is specific to RDBMSes – might not be the kind of dependency you want.

    Why is UoW an anti-pattern sometimes

    I’ll start by saying that everything depends on your use case, but for me – I tend to feel that Unit of Work (but not repository) is an anti-pattern.

    Let me explain why.

    You see, the repository interface can be described in its simplest form as an interface for read/write operations.

    // What a typical repository looks like
    public interface IMyRepository
    {
      MyObject GetById(int id); // Read
      IEnumerable<MyObject> SearchByName(string name); // Read
      void Update(MyObject object); // Write
      void DeleteById(int id); // Write
    }

    I’ll use an analogy here: In good old C, the standard input/output header defines basic read/write operations. The interface <stdio.h> is an abstraction of the underlying data access implementation and is a close example of the repository pattern – the interface doesn’t care if you are writing to the console, to a filesystem, or to a serial port – just like a repository interface shouldn’t care if it was an RDBMS, or a CSV file, or even a remote API call.

    Does <stdio.h> expose specific features of a particular filesystem or a serial port? No. So why should a specific feature of an RDBMS (transaction management) be depended upon outside of a repository interface?

    userRepository.Update(user);
    unitOfWork.Save(); // If this was not done, the record will not be updated!

    Using the C example from earlier: Imagine having to always call fsync() (from <unistd.h>) after calling fwrite() in order to commit changes – does it make sense? Sure – you may call fsync() if you wanted to force your changes to disk immediately, but you do not and should not have to explicitly call it.

    (In .NET there is Transaction Scope, but that is a topic for another day.)

    How much to depend on the RDBMS?

    RDBMSes are great tools with important features, but as an application architect, I often ask myself if it really matters to the application I am building.

    Sure, almost 99% of the time the RDBMS is unlikely to be switched for anything else. Heck, in some applications it may even be the same version of MySQL for the rest of the application’s life – that’s probably because many applications were designed with a database-first approach (and I have another blog article coming up about why a database-first approach should probably be avoided.)

    So you may think, YAGNI – let’s not over-design the application. Maybe in such a case, you may be better off without the Repository/UoW pattern entirely, but… this is 2021, and you are unit testing your code, rightttt?

    Unit tests and mocks

    If you have ever attempted to mock an ORM framework, you’ll know it is pretty impossible. Sure, EF Core has an In-Memory Provider that can be used for testing, but that has a lot of caveats.

    As a result, it would be easier to apply the repository pattern instead of attempting to mock the ORM framework.

    Mocking the test, or testing the mock?

    Ever had unit tests pass but integration tests fail because of database constraints? Ever tried testing code that relied on a transaction rollback? Mocking the behavior of an RDBMS is extremely difficult, and spending half our lives trying to mock how an RDBMS works shouldn’t be the case because we do not want to end up testing our mocks instead.

    // Typical use of UoW
    try
    {
      userRepository.Update(user);
      unitOfWork.Save(); // This commits the transaction
    }
    catch (Exception ex)
    {
      unitOfWork.Rollback();
    }

    Example: How would you mock and test the rollback?

    Instead, we should implement repositories as if we were writing data to regular storage – think of it as writing to a CSV file, memory, Redis cache, or something else.

    Do CSV files have transactions? No.

    Then, maybe we should not use Unit of Work:

    try
    {
      userRepository.Update(user);
    }
    catch (Exception ex)
    {
      // Log an error and fix it manually, retry the operation, place
      // the update in a queue to be processed later if you really HAVE 
      // to make this update, otherwise just throw the exception!
    }

    The generics phenomenon

    The typical Repository/UoW pattern is to make each repository represent a single domain entity, e.g.

    public interface IUserRepository
    {
      public void GetById(int id);
      public void Create(User user);
      public void Update(User user);
      public void Delete(int id);
    }
    
    public interface IGroupRepository
    {
      public void GetById(int id);
      public void Create(Group group);
      public void Update(Group group);
      public void Delete(int id);
    } 

    And as a result, it is common to have these further reduced to a generic interface to reduce the repetition on CRUD methods, e.g.

    public interface IRepository<TEntity>
    {
      public void GetById(int id);
      public void Create(TEntity entity);
      public void Update(TEntity entity);
      public void Delete(int id);
    }
    
    public interface IUserRepository : IRepository<User> { ... }
    public interface IGroupRepository : IRepository<Group> { ... }

    However, quite a large number of applications I have written do not use the full CRUD operations on every single repository. Some tables are read-only, some never require an update, some never get deleted.

    The N-N relationship

    Next, how do I add a user to a group, or assign a group to a user?

    Create yet another repository for the intermediary N-N relationship table and insert a record!

    public interface IUserGroupRepository : IRepository<UserGroup> { ... }
    
    // Usage
    var userGroup = new UserGroup(userId, groupId);
    userGroupRepository.Create(userGroup);
    unitOfWork.Save(); // Always remember to save!

    This is why implementations of the Repository/UoW often end up with a crazy list of interfaces and classes, and it’s probably not easy for a developer to figure out which repository to use. Is it called UserGroup or GroupUser? @#$%^&

    It is also extremely counter-intuitive to be actually creating a new object. In a regular object-oriented code, it would probably be written like this:

    group.AddUser(user);

    Why aren’t repositories written more expressively?

    Unlike <stdio.h> in C that I used as an analogy earlier, repository interfaces are not doing byte-level I/O operations – it handles more complex data types, so methods should therefore be written more expressively.

    For example, why not write the User-Group relationship methods in such a manner?

    public interface IUserRepository 
    {
      public void AddGroup(int userId, int groupId); // Inserts into UserGroup
      ...
    }
    
    public interface IGroupRepository
    {
      public void AddUser(int groupId, int userId); // Inserts into UserGroup
      ...
    }

    Even better – if your application will never ever see the need to store Users and Groups in different data stores, why not simply combine them into one repository interface?

    public interface IAccountServiceRepository
    {
      public void CreateUser(User user);
      public void CreateGroup(Group group);
      public void CreateUserWithNewGroup(User user, Group group); // Can use a transaction
      public void AddUserToGroup(int userId, int groupId); // Inserts into UserGroup
      ...
    }

    (Imagine you were writing to the UNIX /etc/passwd and /etc/group – how would you implement it?)

    One may argue that I’ve come full circle and am replicating a UoW but at the same time also violating the single-responsibility principle. Then again, what is the “single responsibility” of a repository? Often the term “single responsibility” is being taken out of context from what the originator (Robert C. Martin) had expressed it to be: “A class should have only one reason to change”. What external or structural influences may cause the interface above to change?

    Lastly, if CreateUserWithNewGroup() required a transaction for an atomic operation, shouldn’t the transaction be managed at the repository rather than by the domain? Should the onus of transaction management be placed on the domain layer or the repository layer? Is handling transactions in the domain logic also violating the single-responsibility principle?

    Conclusion

    Once again, if your software or company really, really, really depended its life on having consistent data in an RDBMS, by all means, continue to use the Repository/UoW pattern (or simply use the ORM directly since it is a hard dependency.)

    But for a large majority of cases, YAGNI. A single repository interface alone is probably good enough.

    This blog article was more of me thinking out aloud than trying to encourage or influence a change in how people implement repository/UoW, and comments are most certainly welcome.

  • Teachers – the unsung heroes

    Teachers – the unsung heroes

    Not his first “skool”

    My First Skool (MFS) isn’t Ethan’s first school; he had transferred from another private school which shall remain unnamed (although it is noteworthy that it was a well-known franchise.)

    Ethan joined MFS at Nursery level (age 4). At the time, Ethan had just gone through 4 main teachers, 2 principals and countless part-time “assistant” teachers, plus 2 hand-foot-mouth-disease (HFMD) episodes, and it was an incredibly difficult time trying to encourage his return to school. He was excited to go to a “new school” for the first week or two, but his excitement weaned quickly.

    Every parent probably thinks their kid is a genius, and I was no different. Some of his “achievements” include being able to name both primary and secondary colours by age 2, identify almost every single brand of car on the street by age 3, and even discern differences that sets almost similar car models apart (Lamborghini Huracan Performanté vs Evo – I can barely tell the difference!)

    As a kid, he was also incredibly observant, catching details of things we usually wouldn’t notice. I was led to believe he has a photographic memory.

    At 2-3 years, he was also able to remix words into the tunes of nursery rhymes:

    两只car car,两只car car,跑得快,跑得快。
    一只没有headlight,一只没有taillight,真奇怪,真奇怪。

    Ethan, ~3 years old

    However, Ethan had always seemed distracted and restless in class. Teachers from his previous school and also MFS had highlighted this, but over time, his behaviour seemed to have improved – especially in MFS. I would be lying if I said I hadn’t at some point thought he might be ADHD/ADD.

    The bad experience

    It is not until much later now at the age of 6 that Ethan had finally explained why he did not like going to school: his (previous) teachers were very fierce and always scolded him, and he hated that they simply “dragged” him to class. Thankfully, he had not expressed the same sentiments for his teachers at MFS.

    Given his incredible memory for things, it is likely that his early experiences left a scar in his mind that school is “not fun”. He might, at some point, have even been traumatised by the constant change of faces and treatment from different teachers when at the age of 2-3 children are mostly seeking security from familiar faces and battling separation anxiety.

    I am glad that Ethan’s attitude towards school has improved ever since he entered MFS – with much credits to teacher Stefanie, Zhou 老师 and teacher Celine. While most of Ethan’s previous teachers only complained of his inattentiveness and restlessness, there was only one other teacher from the previous school (who eventually resigned) who observed that Ethan was a little different – similarly, the teachers at MFS then expressed that while Ethan seems distracted, he is actually listening and capable of answering questions afterwards. Of course, I understand that his restless behaviour is, however, a distraction to both the teacher and classmates, and needs some work.

    Finding MFS, and the dedicated teachers was a matter of sheer luck during a time of desperation as I sought to get Ethan out of a disastrous pre-school experience, but should not be taken for granted. MFS (or at least the branch Ethan is in) had the operational scale and capability to provide a good support structure for the teachers, and that has helped the teachers remain dedicated and committed. With scale, it makes manpower planning easier. With scale, it is able to hire more teachers or assistants to relieve workload when required. With scale, teachers have a growth path in their career.

    The primary school rush

    Ethan will be enrolling for primary school in a few weeks’ time. I am more convinced than ever that it is not necessary to get into a name-brand school, and could never understand the kiasu-ism that surrounds P1 registration. I believe that in every school there will be good and bad teachers. As long as there isn’t terrible peer influence (which is more a function of the location/neighbourhood than the school’s reputation), any MOE school in Singapore is possibly a decent school.

    Not shown accurately in P1 registration data is the population of the applicants in the neighbourhood versus the capacity of nearby schools. The popular schools in each neighbourhood tend to have a disproportionally high oversubscription ratio which feeds on itself year after year, and has no direct correlation to the actual quality of the school’s teaching staff, management and curriculum delivery style. For example, Clementi is an incredibly dense neighbourhood, and no doubt Nan Hua Primary may be a good school, the high oversubscription ratio might be just a function of town population.

    I was paying almost 3 times more (before subsidy) in a private name-brand franchise school, which is clear that even “throwing money at the problem” could not guarantee my child’s early formative school experience. Fancy award-winning programmes, music and dance, arts, whatever it may be, had little actual effect on the child’s development if teachers are overwhelmed or have no passion in their work.

    It is my belief that a school that is not overwhelmed with students and demanding parents will likely function better, and hence, using a similar logic – an over-subscribed primary school may not always be better. Of course, there are arguments that lousier schools have other things to deal with – such as bad students – but what makes one think that good schools don’t have bad students?

    The role teachers play

    Teachers should not underestimate the impact or influence they have on every child. Children spend most of their waking hours in highly developed countries like Singapore in school. Pre-schoolers are in school for an average of 45-50 hours per week – more hours than average adults are at work.

    Teachers work even longer hours then – they have to prepare for materials, be in school before kids arrive, and leave only when the last child has left. Rain or shine, COVID or HFMD. Passionate, dedicated and committed teachers like Ethan’s teachers at MFS are the under-appreciated unsung heros of our modern, highly-educated society.

    I, too, hope that one day I can become a teacher and contribute back to society. Without our education system and teachers, Singapore wouldn’t have been what she is today.

    Wishing all teachers and those in the education profession an advanced Happy National Day and a Happy Teachers’ Day 2021 – thank you!