Miscellaneous Topics in Computer Science

Multi-platform – a program is said to be multi-platform if it is able to run on multiple platforms, or operating systems. Sometimes, the same script or program will be able to run on different OSes with the same exact file and no changes whatsoever. For example, a lot of Python or Java software can run on macOS, Linux, and Windows, without any modification (note that there are some platform-specific things you can do in Python or Java, but it’s also possible to make software in these languages that is completely multi-platform with no modifications). But sometimes, multi-platform software will have separate versions – one for macOS, one for Linux, or one for Windows. They are all roughly the same in terms of features, but they might be ported or use some OS-specific things like DLLs, syscalls, or APIs.

Back in the day, software used to be more closely wedded to the hardware, but now, multi-platform is important. If you make a mobile app, it needs an Android and an iOS version, even if you personally have a preference for one over the other. If you make a computer program, it needs to be available for Windows, macOS, and Linux. Of course, the easiest way to make something multi-platform – available on all OSes for computers and phones – is by making a web app rather than a native app. It’s also possible to create a back-end API that all clients deal with, and then the native apps just connect to the API. So if you have an app that requires network connectivity, both the Android and iOS apps can connect to the same API and use your network-related features that way in order to get a consistent experience (not counting differences in the front-end user interface, like buttons and whatnot).

Coupling – how dependent things are on each other. Tight coupling means high dependence, like one thing being 100% reliant on another specific thing, while loose coupling means things are more independent. For example, microservices are loosely coupled, meaning one container is not super dependent on a particular container. A container in microservice architecture might need to interact with a container of another type, but it doesn’t need to be any particular one, just as long as it’s a container. If you need to search on an ecommerce site, you need a search microservice, but it doesn’t matter which one exactly. By contrast, tight coupling would mean that there is only a single search server, and all search requests would need to go to it and nothing else.

JIT compiling – interpreted languages still involve compiling, but it’s just-in-time (JIT) compiling, meaning it compiles right before you need to use it. Unlike a C/C++ compiler, which compiles things way ahead of time, interpreters use JIT compiling. This means that, unlike a traditional compiled language, you can run your program without having to wait for the entire thing to compile. It does, however, mean that it might be slower and less efficient. Performance is one common criticism of Python. However, because of the extra convenience for software developers, people still use it anyway.

Fuzzing – sending invalid or random inputs to a program to see how it responds. Fuzzers are used to send lots of automatic input to a program in hopes of eventually finding something that will hack it, like finding a remote code execution vulnerability, or something like that. Fuzzing is dumb yet automated, so you can send lots of simple/unsuccessful stuff and it doesn’t matter, because a computer can do it very quickly. Even if 99.9999% of the automatic/random stuff from a fuzzer doesn’t work, the small amount of times that it actually finds something meaningful makes it all worthwhile. The more computing resources you have, the faster you can find vulnerabilities via fuzzing. American Fuzzy Lop (AFL) is a well-known fuzzing program, a.k.a. a fuzzer.

Watermarking – putting personal information in files or programs so that you can tell who distributed it if it gets shared on file sharing/piracy sites, such as torrent trackers. This is for premium software that you charge money for and don’t want your customers redistributing to others free of charge. So if someone named John Doe buys a copy of your premium software, there will be a hidden watermark within the software that says “John Doe bought this software and isn’t allowed to redistribute it” or something like that. This is so that piracy is discouraged to begin with, but a company can take someone to court if they share their copyrighted intellectual property.

Zhaoxin – a new and emerging Chinese tech company that makes x86 CPUs. In the future, it might compete with Intel and AMD.

LTS – Long Term Support. An LTS version of a Linux distro will be supported (software/security updates) for longer than a non-LTS one. Some people criticize LTS distros for being older, but I prefer them because it means you don’t have to reinstall an OS quite as often, since longer support means fewer times you need to upgrade.

EOL – End Of Life. When software is EOL, it will no longer get support. That means no more fixes for bugs or security vulnerabilities. When your app uses third party dependencies, keep up with the news for them. When a dependency you use has an announcement on its website that says it’s going to be EOL at a certain date, you should migrate off of it to something else before then.

Some people like open source projects, but one benefit of proprietary over open source is that you can have a support contract for a certain amount of time, which will ensure that you will get support. But if you use something open source, like a flash-in-the-pan JavaScript framework, it could disappear overnight and you’d have no recourse. It’s free, so you’re not guaranteed anything.

Keygen – back in the day, a lot of proprietary software would cost money to buy, and then you’d get a serial number, or software key, which you’d have to put into the program in order for it to work. A key could only be used by one person. This was to combat software piracy. However, some people disliked the idea of paying for software, so reverse engineers would take a premium program, run it in a disassembler program such as IDA Pro, and then figure out the acceptable key algorithm. Then they’d make a program called a keygen, which generates keys for the premium software. A keygen lets someone use a program without paying for it. This is illegal copyright infringement. Not only that, but sometimes a keygen would be distributed through a file-sharing service, and would often be bundled with malware, such as a keylogger or RAT.

Although keygens are illegal, there is a legal educational tool called a “crackme” which is used for learning about reverse engineering. A crackme is a program that you’re intended to reverse engineer so that you can find the key for it. Reverse engineering is illegal in some cases (such as if you were to try and reverse engineer Photoshop to make a keygen), but for crackmes, it’s legal. And a legal and very useful use of reverse engineering is for malware analysis.

These days, keygens and watermarks are less common, because a lot of software uses cloud and user accounts, so a lot of software now runs on a remote server rather than in a standalone client application. And a lot of software has newer business models, such as freemium/microtransactions, ads, service subscriptions (like VPNs or cloud storage), or selling user data, rather than only charging money for offline software.

TODO – a comment in your code that reminds you of something you should add or fix later. It means “note to self: you ought to do this later.”


//TODO: validate file structure before proceeding with parsing

Sometimes, you know what you eventually want to do, but you don’t have the time or patience to do it right then and there. It can take just a few seconds to think “oh, I should probably fix/impove that” but it could take many minutes or even hours to actually implement the change. A TODO is a quick reminder of your intent for the future. An alternative to TODO comments is GitHub’s “Issues” feature. On a given repo, you can open a new issue, which can function similarly to a TODO, though it can be used for other things too, like bug reporting.

The good thing about putting TODO in front of a comment about something you want to do later is that you can just ctrl+f to find all TODOs to keep track of them. There are better ways of bug-tracking or managing goals, but TODOs are a quick and easy way to do things, even if some people argue that they’re not a best practice.

Fun fact: there are thousands of TODO comments in the source code of Linux.

FIXME – similar to a TODO, a FIXME comment means that something is messed up and needs to be fixed. While a TODO might be an idea for a future feature to add, a FIXME is all about bugs.

Test-driven development – write tests before you write your code. If you want to write code that adds two numbers, first you write a unit test which uses the adding function and then tries an example of adder(2,2). If the return value is 4, then it passed that unit test. Then, after you write your adder function, you run the unit test on it. TDD is all about planning and thinking of how you expect your code to behave, and verifying it with tests. But instead of thinking of testing as an afterthought, TDD is all about prioritizing tests above all else.

That being said, it’s possible to actually write incorrect unit tests, or to not test enough things within your program (such as edge cases), so passing the unit tests doesn’t mean it’ll be perfect!

Profiling – monitoring and measuring the performance of your application. This is especially important in web development. A coding student who has a website that gets 5 viewers per month doesn’t need to do profiling, but a big company with lots of users certainly does.

Headless server – a server with no keyboard, mouse, or monitor attached. It is only controlled remotely, such as through a web-based interface or SSH.

Manifest file – a file, often XML, containing metadata about files within a project.

Shipping code – to ship code means to deploy code to a production server to give it to a customer.

Push to production – put the latest version of your tested code to your company’s servers that your customers use. Making changes directly to production is very bad. The usual course is as follows:

Development -> testing -> production.

Turnkey solution – something that works immediately (“out of the box”) with little to no configuration. For example, you could manually configure a pf firewall, or you could use pfSense, which is an out of the box firewall solution which is configured for you.

Faraday cage – a device which blocks all wireless communications. If you put a phone in a Faraday cage, it can’t connect to cell towers, wifi, or Bluetooth.

Van Eck Phreaking – a way to see what’s on someone’s computer screen by analyzing radio waves. Screens leak radio waves, which can be measured. There is a cool program called TempestSDR, which can be used with a computer that has a software-defined radio (SDR) device plugged in to it, in order to see what’s on someone’s screen, even through walls. It might sound like science fiction, but it’s not. You can even see a demonstration of it here: https://www.youtube.com/watch?v=BpNP9b3aIfY

That being said, one limitation of it is that the picture quality is terrible.

Side channel attacks – just like how monitors/TVs/screens leak radio waves, CPUs do too. A side channel attack is when someone uses a device that can measure radio waves, and an oscilloscope, in order to analyze the RF leaks from a CPU, and then use that to figure out what is happening on it. So you could do things like see the CPU instructions that are being executed on a CPU, and I’ve heard that this can be used for thwarting some cryptography-relateed stuff. It’s very complicated stuff, but the long story short is that CPUs leak information that can be measured by other devices. Many electronic devices are noisy. If bad people analyze that noise, that can lead to security/privacy issues.

Plugin, add-on, or extension – a piece of software that doesn’t work on its own. Instead, it is installed within another program, and gives it new functionality. IDEs, browsers, and CMSes are examples of programs that support add-ons.

SPOF – Single Point Of Failure. You generally want to avoid SPOFs, especially in enterprise infrastructure.

Dependency injection – injecting objects into a class instead of creating them within the class. DI can be achieved through the use of constructor arguments for non-primitive objects.

Esoteric programming languages a.k.a. esolangs – they’re not useful in a real-world situation, but they can be funny or interesting. Here’s a wiki for esoteric languages: https://esolangs.org/wiki/Main_Page

CPU architecture – the kind of architecture of a CPU determines its instruction set and what software can run on it. Desktops and laptops are x86, or x86_64/AMD64, whereas phones are ARM architecture. Windows is primarily x86-centric, but Windows IoT core can run on ARM Raspberry Pis, though you can’t run all your typical x86 programs on them. When people say x86, they sometimes mean the 64-bit version of x86. It’s just easier to say x86 than x86_64 or AMD64, but they all basically mean the same thing. Microsoft also recently made a tablet that runs Windows on an ARM CPU, but it only emulates 32-bit x86, and not very well, either. So ARM Windows breaks compatibility with a lot of older Windows software.

RISC-V is a new and open source architecture which could either be promising or just vaporware. Vaporware means tech that gets announced and then never goes anywhere. There is an electronic music genre with a similar-sounding name – vaporwave. But that is not related to vaporware at all.

MIPS is another architecture worth knowing about. Some classes about assembly use MIPS instead of other architectures. Some MIPS devices include routers and phones. My current router is ARM-based though, but older ones are MIPS. MIPS is still prominent in academia though, because it lends itself well to being taught. More useful real-world architectures can be harder to learn.

Other architectures include PowerPC and SPARC, but they are less common. x86 and ARM are the most common by far these days… at least for now.

There are many different kinds of assembly language, such as x86 assembly, ARM assembly, and MIPS32 assembly. They are not the same. They run on different CPU architectures, though you can use software to run it on something else. For example, you can use SPIM, a MIPS simulator, to run MIPS code on an x86 computer. You might have to use a program like that if you take a computer architecture class. Computer science classes that teach about assembly and CPU architecture are sometimes called “computer organization.” They are usually taken in the sophomore or junior year.

People who want to get into reverse engineering or malware analysis will need a solid understanding of assembly, as disassemblers take compiled code and turn it into assembly. Compiled code can’t be easily un-compiled, so the only way to make it even slightly readable is by displaying it as assembly. However, assembly isn’t as easy to read as higher-level languages, so reverse engineering a compiled program is stringent and time-consuming process even with a disassembler program. Imagine taking a pie and trying to figure out all the exact ingredients and steps that were used to create it because the person who made it refuses to give you the original recipe. It’s not easy.

Computational complexity – how long it will take for a program to run. Complexity theory is the study of computational complexity.

Big O notation – O(n), O(n2), O(log n), O(1), O(2n) are examples of possible time spaces for how long it will take an algorithm to run, whether you’re talking about worst case, average case, or best case scenario. The n is the amount of items, such as items in an array, or just data in some other format. Big O notation is often associated with control structure concepts like loops, as well as data in arrays. For big O notation, you can disregard terms in a polynomial aside from the dominant term (the term with the highest exponent). You can also drop coefficient if it’s constant. In the example of 5x, 5 is the coefficient of the term x. So something with a runtime of O(5n2+3n+1) simplifies to just O(n2). If you’re familiar with derivatives in calculus, it’s sort of like that , but not exactly the same. But in derivation, the dominant term gets reduced and non-dominant terms are just smaller, whereas in big O, the dominant term stays unchanged and you drop all non-dominant terms, even if they involve n rather than a constant.

Here is the order of big O speed, from fastest to slowest:

  1. O(1) – constant time
  2. O(log n) – logarithmic time
  3. O(n) – linear time
  4. O(n log n) – quasilinear time
  5. O(n2) – quadratic time
  6. O(2n) – exponential time
  7. O(n!) – factorial time

If that all confuses you, think of it like this: the way you write your code can change how long it takes your program to run. And n represents the amount of data you have. So more data will take longer, but the way you write your code determines how much longer it takes with said data.

There is some math you can do to compute the big O notation of any given code, but that’s beyond the scope of this book.

If you’re only working with a really small amount of items, then optimizing your algorithm to get better time complexity doesn’t matter too much. But let’s say you want to write a program that deals with a ton of things, like maybe you’re writing a program to find all primes from 1 to a trillion. When you’re dealing with a lot of stuff, then algorithm efficiency really does matter. The speed of your algorithms can matter more than the speed of the computer the code is running on.

Time complexity – how much time it takes to run an algorithm. A sub-field of computer science related to this is computational complexity theory, also called complexity theory.

Space complexity – how much memory you’ll need for an algorithm. In computer science, time complexity is much more commonly discussed. Space complexity isn’t mentioned that much.

Lambda expressions a.k.a. anonymous functions – usually, functions have names. But lambda expressions do not. This is a concept from functional programming, but you will still see it in mostly OOP languages like Java, Python, and JavaScript. You can sort of have functions as objects, though the implementation varies based on the language. One thing lambda expressions are useful for in Java, at least in my personal experiences, is for event handlers for GUI programs, such as when someone presses a key or clicks on a button. When a button is pressed, the code in the event-handling lambda is run.

Branch prediction – when there is conditional logic, like if/else if/else, program execution could go one of many different paths. But in some cases, even though there can be multiple choices, one of them is more likely than the others, and you can do things to compensate for that increased likelihood.

A real-life example of branch prediction is gas stations. You can choose between regular, mid-grade, and premium gas. Mid-grade is just a mixture of regular and premium. You, the customer who wants gas for your car, can make one of 3 different decisions when you go to the pump. However, because regular is the most common choice, gas stations have more regular than premium. This is because they are predicting the choice you will make, which impacts which branch you go down. You can still choose to get premium gas, but they are assuming, based on data, that most people are going to buy regular gas instead. Taking time to figure out branch prediction on a processor is called speculative execution. An incorrect assumption is called branch misprediction.

Serialization – turning an object state in RAM into a byte stream, usually used with a file, networking, or a database entry. An example of serialization is saving data to a file on a hard drive.

Deserialization – the reverse of serialization. It’s when you take a byte stream and put it into RAM for an object state. There are some security attacks related to deserialization. An example of deserialization is loading data from a file on a hard drive. Serialization and deserialization are also associated with databases, not just file storage.

If you want to perform serialization or deserialization in Java, then you might need to implement the Serializable interface like in the class:

SomeClass implements Serializable

Finite state machine – simply put, a finite state machine is a system which can only have a fixed number of states. It can only go from one finite state to another. A simple example of a finite state machine is the transmission in a car. You can be in reverse, neutral, first, second, third, fourth, and fifth gear. It’s not possible to be in 7th, 8th, 9th, 1,000,000th, and so on. You also can’t be in gear 1.001, gear 1.002, or gear 1.003. There’s a fixed number of states, and you know all of them. If there were infinitely many possibilities for gears, you couldn’t call it an FSM anymore. FSMs are used in software.

Process – software running in RAM. A single program might have one process, or maybe even multiple ones. For example, Chrome has a separate process for every tab, so that if a single tab crashes, it doesn’t take the whole browser with it.

Inter-process communication (IPC) – communication between multiple processes. Your first programs will be simple and will only involve a single process, but eventually, you might make very complex things, and then IPC will be handy for you. But not for starting out. One way to achieve interprocess communication is through the use of message queues.

Event-driven programming – the kind of programming where program execution is dependent on event handlers, like if the program has a GUI with buttons and whatnot. Then the computer waits until the user clicks on something or presses a key. Those actions are called events, and they’re what drive this style of programming. Not all programming is event-driven though.

In an overly-simplified explanation, you can think of it as like every button corresponding to a function call. You are running the functions when you press the button to start them. If there are text boxes where you can write things, they might be feeding arguments into a function, or a function might use some getter method (like textField1.getText()) to retrieve the contents of a text field object.

Hardware acceleration – computers have CPUs and GPUs. CPU stands for Central Processing Unit, and GPU stands for Graphics Processing Unit. Graphics cards a.k.a. video cards have GPUs. However, even if you don’t have a dedicated graphics card, you will have what is called an integrated graphics processor, or IGP.

By default, your code will only run on a CPU. Some tasks are only suitable for CPUs, while others can get noticeable performance benefits from using a GPU. GPUs have lots of cores, which means they can do parallelizable tasks very well. The only catch about that is they have to be workloads that can be broken up into smaller pieces that run side by side rather than one after the other. Not all workloads can be divided up this way.

The process of adding GPU support to your code is called hardware acceleration. It adds much complexity to your code, but depending on what you’re doing, the performance gains might be worth it. 3D video games almost always use hardware acceleration. Sometimes, things like video rendering can use hardware acceleration too. Another example of a hardware-accelerated program is Hashcat, which is used for cracking password hashes.

If you want to write desktop software that can make use of a GPU, look into something called OpenCL. However, I don’t recommend that for beginner developers. If you want to make browser-based games with hardware acceleration, look into WebGL, which lets you do higher performance 2D and even 3D graphics in a web page, making use of the user’s GPU.

A lot of software lacks hardware acceleration simply because it’s too complicated and time-consuming to do. In the world of software development, time is money, so unless an app absolutely needs more performance, they won’t bother adding it.

Library – a collection of reusable code that you can put into your project instead of having to write it all from scratch. Libraries contain things like functions and classes. If you want to build something, you can go to Home Depot to buy premade tools and materials. You don’t make every single thing from scratch because that would be a big waste of time. A lot of the time, you’re just putting premade things together, with glue, screws, bolts, or welding. You can modify things, like with drills or saws. Programming libraries are the same way – before writing something, you can see if someone else already made what you want. However, be warned: sometimes, there can be issues with using libraries due to licenses. Not all libraries are free, and some are free, but the licenses have caveats about how you can use them, so it pays to research that well before doing it. Some software uses a dual-license model, where it’s free for personal or educational use, but costs money for commercial use. Some software says you can’t use it for commercial purposes at all. Others say you can use it for commercial or non-commercial purposes, but you have to credit the author and also make the source code publicly available if people request it.

A good rule of thumb is to use built-in stuff that comes with a programming language before resorting to using a third party library.

One library example is Apache Commons. Apache Commons is a collection of Java libraries for different purposes made by Apache, the same group that made the Apache web server, which is used for tons of websites, and Apache Maven, which is a popular build automation tool which makes it easier to keep track of code dependencies. One library from Apache is the Apache Commons IO library, which has tools that are useful if you’re writing code that deals with files. Apache even has their own software license, and you can use Apache-licensed open source software for free, even in commercial products, which is nice. This is much more permissive than many other open source licenses. But just remember that not all libraries use the Apache license.

SDK – software development kit. An SDK enables a developer to make something. You don’t make Android apps on an Android phone, but you can use a regular Windows 10 computer with the Android SDK to make them. SDKs are especially useful for devices where you can’t code on the device itself. You can use Windows to write Windows programs, but you can’t use a PlayStation to make PlayStation games. So that’s why Sony has SDKs for developers.

Wrapper – if you put something in something else, it’s called a wrapper. Some “apps” are just websites with a wrapper, such as Electron, which lets people make computer programs with web technology. Slack is a well-known example of an Electron app. People complain about the poor performance and high resource usage of these kinds of programs, but it can still be beneficial, at least on the developer end, because they are familiar with web development and can deliver a consistent experience across multiple platforms without much OS-specific code. React Native is a similar example. I once heard someone succinctly sum it up: they’re “browsers that can only visit one website.” Wrappers are somewhat controversial and disliked by many, but that doesn’t stop people from using them.

Metaprogramming – writing code in a program that changes other programs (running in RAM) as opposed to things like user input or files. I’ve personally never done any metaprogramming, but people I’ve spoken with who were into Lisp (a functional programming language) talked about it. If you want to get into functional languages, it’s something to look into.

Rainbow table – a table of all values for something, going through every possible combination or outcome. A times table is an example of a rainbow table. This is a tradeoff between storage space used and computational complexity, like CPU usage. If you have a lot of storage space and don’t want to use many CPU cycles, a rainbow table is the way to go. These days, many things are the opposite – relatively small, but requiring higher CPU usage. Rainbow tables are often associated with hashing algorithms and password cracking.

Quine – a useless but intriguing program that only outputs its own source code in its entirety. They are used for programming exercise or intellectual pursuit rather than being useful in the real world. There are many ideas like this. It helps you stay sharp by practicing fresh programming challenges.

Kata – sometimes programming exercises are called kata. If you google kata, you will find websites that have ideas for exercises you can do. Some of the exercises are daily. Katas are to programming as math problems are to math.

Deadlock – when multiple things depend on each other but can’t proceed because of the other ones using something. Let’s say you have a program with a function that needs to use file A, and it’s currently using file B. But it can’t proceed it file A is in use. You also have another function that is currently using file A and wants to use file B, but it can’t proceed until the other function stops using file A. Because they are both waiting on each other to stop locking resources, they can’t proceed, and thus all activity halts. It’s also kind of a Catch-22.

Human-computer interaction – the field of figuring out how people react to software. HCI is used to see how people respond to tech, and what can be done to improve the experience. It is less technical and more about researching people. Software developers know how to use their own software, but the average user might have trouble. Human-computer interaction helps to bridge the gap between developers and users to make people better able to design and create software that more people can understand and use.

When software is only designed by executives in a board room, rather than with the consideration of HCI research, there will be a disconnect between what people want and what they get.

Natural language processing – a sub-field of computer science concerned with understanding language, such as written text. A concept related to NLP is n-grams. The meaning of words depends on their context. The n in n-grams means the number of words, and gram means word. So bigram means you need at least two words (including the unknown word itself) in order to derive the meaning of a word based on the context. A trigram means you need at least 3 words to understand the disambiguated meaning based on context. It can take many words to understand context, so the general term n-gram means words that need n number of words in a phrase in order for the meaning to be known.

A variable means the same thing no matter what, but words in natural languages like English aren’t that simple. For example, here are the words “flies” and “like” in two different contexts with different meanings:

Time flies like an arrow.

Fruit flies like a banana.

People can understand context without even trying. But it’s more complicated for computers.

Microservices – just like how you might break a program up into separate classes and methods, you might want to break up a server into multiple servers, across multiple machines. This helps for scalability. Microservices are often associated with containers and container orchestration, such as Docker and Kubernetes. If you have a webscale (lots of users) online store, you might have separate microservices for logging in, product search, static content delivery, shopping cart, and checkout. There can be multiple instances of each microservice. Each microservice is separate but can interact with the other ones if need be.

Nanoservices – when you break up a monolithic app into way too many tiny pieces, it’s called nanoservices. This is not a good thing. Breaking up one thing into too many small pieces adds unnecessary complexity. Microservices break up one big application into some special purpose services, but not to an extreme extent.

Threads – these days, instead of having super fast single-core processors, we have reached the limits of how good a single core can be, so the solution for CPU manufacturers has been to add more cores. Cores are like mini-CPUs. If you can use threading in your code, you can make it run faster by having the workload broken up into pieces that the different CPU cores can read. The process of using multiple threads is called multithreading. A thread isn’t a core, but they are similar concepts. Intel has something called hyperthreading, which means two threads per core.

While there are performance benefits to multithreading, it also makes development much more difficult. When you’re new to coding, you won’t be dealing with threads at all.

Not Invented Here syndrome – some companies like to reinvent the wheel just because they can, or because they don’t trust other people. When a company makes stuff that already exists, just so they can say they did, it’s called Not Invented Here, or NIH syndrome. Google created the Go programming language because of their NIH syndrome. They don’t trust other stuff for their company because “it was not invented here.”

Histogram – when you’re dealing with data, you might have multiples of a single result, such as if you make a program to count the number of times each letter in the alphabet showed up in a text file. A histogram shows the frequency of occurrences.

Histogram example:

A *****

B **

C ***

D *

E ***********

Pseudocode – not real code. You write pseudocode to explain a concept or quickly get the gist of what you want to do. Some people make jokes about Python and say it looks like pseudocode, but in a way, that’s good, because it’s easy to read and write, and pseudocode is a solution to the problem of code taking longer to write than it takes for you to forget your ideas. People write down their logic ideas in pseudocode first, then code later, because many languages can take a long time to implement something. Java, for example, is an incredibly verbose language, and you can quickly write 100 lines of code that do very little. By contrast, a language that looks more like pseudocode, such as Python, means that there are fewer lines because each line does more stuff, and does it simply and elegantly – unlike Perl, which is ugly and unreadable.

Cargo cult programming – when you copy and paste code after finding a solution on Google or Stack Overflow instead of solving it yourself. When you do that all the time, all you’re learning is how to copy and paste, not how to solve problems. Sometimes, when you’re stuck, it can make sense to look on Stack Overflow. But if you do it too often, that’s a bad thing. Try official documentation first. Moreover, even if someone has an answer on Stack Overflow, you shouldn’t copy it in its entirety.

It’s a really bad idea to copy and paste code when you don’t understand what it does. A cargo cult programmer is someone who is good at googling and knows that the big chunk of code they copy does a certain task, but they don’t know what the individual parts of it do.

Idiomatic programming – there’s a lot of general, multi-lingual programming theory. However, when you get deep into a specific language or framework, you will learn about idiomatic programming, which is stuff that is specific to that particular thing you’re using. Idiomatic programming might involve technical minutiae that you won’t learn about with beginner-level tutorials which only scratch the surface. For example, in this book, I mention overarching file IO concepts, but I didn’t go into precise details, such as the Java API’s more in-depth stuff like BufferedOutputStream or BufferedInputStream. But knowing idiomatic Java programming won’t help you much with Python, for example. Idiomatic programming skills are usually non-transferable, which is why it’s better to learn fundamental, timeless concepts first.

Browser extensions – in addition to web apps, desktop apps, and mobile apps, you can also create browser extensions, such as for Firefox or Chrome. If you use WebExtensions APIs, you can make a single extension that should be able to work on multiple browsers with some small changes. Browser extensions add functionality to a web browser. The most prevalent types of browser extensions include ad blockers, security add-ons, and password managers. In my personal experiences, I have used browser extensions for macOS, Windows, and Linux – for Firefox and Chrome. On Android, I’ve used Firefox Mobile, which allows you to install the same add-ons you’d use on a computer. But iOS is a little more limited because Apple doesn’t allow for browser add-ons. All they let you do is block content, such as for ad blockers or parental controls. You can’t add more features to a browser though. But aside from iOS, all other platforms can handle browser extensions just fine.

Maybe you want to make a browser extension. If you do, look into WebExtensions. Mozilla’s MDN Web Docs site has useful information about how to start.

Computer vision – the ability for computers to process images and figure out what the content of the image is. Some examples of computer vision include what’s needed for driverless cars to operate, as well as a project called Transient Tracker, which is software that detects asteroids in images from space telescopes. Google’s “search by image” feature, a reverse image search, also does image recognition.

Optical character recognition – software that can process images of handwriting or printed writing and then turn it into digital text.

Distributed systems – systems that span across multiple devices. Some examples of distributed systems include botnets, supercomputers, and torrents (peer-to-peer file sharing). It’s harder to deal with distributed systems as opposed to centralized stuff on a single computer.

Serial – one thing after the other. Serialization is turning data into something serial.

Parallel – multiple things side by side at the same time. Older kinds of computer cables are parallel, but newer ones are serial. Parallel processing can mean taking a big workload, breaking it into smaller separate pieces, and then having different processors do a different piece of it separately.

Race conditions – in software, things often need to happen in a certain order. But sometimes, something will take too long, and then things will happen out of order.

Let’s say you want to have ice cream for dessert tomorrow, but your fridge is broken. So you need to do two things: you need to hire someone to fix your fridge, and you need to buy ice cream. So you call an appliance repair specialist, and they schedule a time to fix it for you. They say they’ll come to fix it at about 4 pm. They say it will take them an hour to fix it. So you know that, if you leave for the grocery store at 4:30 pm, you will be back with the ice cream at 5:00 pm, which is about the time they’ll be done fixing the fridge. The fridge/freezer needs to be fixed before you get back with the ice cream.

Unfortunately, when you come back, they aren’t finished. The person repairing the fridge says they need to order an extra part, which will take more time. The ice cream melts because you have nowhere to put it. Mission failed. Sometimes, when things don’t happen in the right order, or at the right time, the outcome can be incorrect. This is also true for computers. Because some things are time-sensitive, such as ice cream that will melt if it doesn’t get put in a freezer, things are said to be racing to be done in the right order. The repair specialist tried very hard to fix it quickly, but it just didn’t work out. They lost that race. That’s why they’re called race conditions.

Race conditions are often associated with multiprocessing because you have multiple separate threads. One might finish faster than another, which can be potentially out of order or give the wrong result. One thread might be contingent on another thing finishing before it gets to a particular stage of program execution, and that might not be guaranteed to happen.

Multiple threads might have shared resources between them, and if something gets accessed at the wrong time, such as before or after something changed, then one of the threads can have the wrong data.

You can fix race conditions with locks. However, locking can lead to its own problems, such as deadlock, where multiple things are waiting on each other to finish, sometimes in a circular way, where nothing can proceed because of the locks, halting all progress in the program.

Cellular automata – usually a 2D grid, or 2D array, where each element is a cell, hence cellular automata. “Automata” means multiple automatons. An automaton moves on its own. It is a simple form of a complex system, with simple rules and cells, but it exhibits what is referred to as emergent behavior – complex things that arise from a lot of simple things. Conway’s Game of Life is a very well-known example of cellular automata, where you can place cells on a 2D grid, and there are “generations” where cells either die, stay, or are spawned in empty spaces, depending on the number of adjacent live cells (called neighbors). These different possibilities are called states. In this example, there are two states – something being in the cell, or the cell being empty. Generations are like turns in a turn-based game, except things happen automatically and there is no user input, except for initially placing some cells in Conway’s Game of Life. There are also some variations where you’re allowed to place more in further generations.

In most cases, implementations of cellular automata involve things changing even when you personally don’t do anything. Because the cells change on their own, they are automatic. Because they’re automatic, you can call them automatons, hence cellular automata. It’s like a simulation. The rules are straightforward, but very cool and intricate patterns emerge when you play it, as long as you put the right combinations of cells and spaces in it.

Scheduling – your OS lets multiple programs run “simultaneously” (not quite) by scheduling CPU time. Just as a network involves breaking up things into packets, scheduling breaks up tasks and lets one thing run, then the other, switching back and forth very quickly. This way, it can look like multiple things are happening at the same time, even if they really aren’t.

Parallel processing – things running side by side. Processors have multiple cores, so you can have multiple threads to make use of the hardware. Graphics cards have many cores, so they lend themselves well to parallel processing. Programming for parallelization is more complicated than non-parallelized stuff though. You will want to start with single-threaded stuff. Some software isn’t optimized for multi-core processors, and so single-thread performance of CPUs makes a difference. However, CPUs haven’t gotten faster and faster like they did in the past, so the way to make up for the lack of significantly faster CPUs is to add more cores, and to make software make use of said cores. For many years, there were consistent gains in performance, back when CPUs only had one core. But nowadays, that has slowed down. So with multi-core processors, you essentially break up your task into multiple smaller chunks and have each one done on a different core.

Concurrency – a topic that is similar to parallelism, but not exactly the same. The OS’s process scheduler can let one thread run for a little while, then another. It goes back and forth. This can be done even on a single core rather than multiple cores. The processes are interleaved.

Cryptography – making something hidden to anyone but the intended recipient or owner. This involves complicated math. A mathematical algorithm for encrypting data is called a cipher. Any site with HTTPS uses encryption for network traffic. There are two main types of encryption: data-at-rest and data-in-motion. Data-at-rest means data on a hard drive or SSD, whereas data-in-motion is data that is transmitted over a network, such as web traffic. No encryption is 100% uncrackable. Over time, computers get faster, which makes cracking encryption easier, guessing every possible combination for decryption keys. Not only that, but every now and then, someone finds a flaw in an encryption algorithm that means you don’t need to try all the combinations of keys to decrypt it.

Exhaustive enumeration a.k.a. brute-forcing – trying every combination to do something. For password brute-forcing, it means trying every combination of letters or perhaps using a dictionary file instead. Brute-forcing a problem in programming means to use a guess-and-check method over and over again until you get it right. Brute-forcing is a kind of primitive way to try and solve a problem, but if you have enough processing power and the problem is small enough, you can do it. The longer a password or encryption key is, the longer it will take to brute force it.

Another enumeration-related term is host enumeration, which means when a hacker or security auditor (called a pen tester) discovers all the devices on a given network.

Caching – putting stuff in a place that will make it faster to access. You can use server software like Redis or Memcached to put certain things in RAM to make it faster, as opposed to having things on a hard drive or SSD, which is slower to access. Caching can also mean putting something into an object or array or whatever where it will be easier to look it up later.

Caching is important because even if your algorithm or app does what it needs to do, and it gets the right answer or output, it might be too slow. You can try to optimize algorithms, but eventually, you’ll reach a point where that doesn’t get any faster, and the only way to speed things up is with caching, because there’s a limit to how fast something can be if it has to be read from a relatively slow hard drive or SSD. RAM is so much faster than either of them.

A topic related to caching is memoization. That’s not a typo – it’s not memorization, but memoization. If something in your program takes a long time to do, and you might need it multiple times, you can cache the result so that you don’t have to waste more time repeating the same thing over and over again. Memoization is especially useful for recursive algorithms.

Technical debt – when you cut corners and write software in a sloppy way, you might be saving yourself half an hour in the short-term. Maybe it’ll help you meet the current deadline. But you are creating technical debt which you will have to spend a lot of time in the future fixing. Cutting corners and making your software poorly-written and non-extensible might mean having to waste dozens of hours in the future. Aside from bad code, another way to create technical debt is to not write comments or documentation because then other people working on the project will have to waste a lot of time trying to figure out what the code does, or perhaps even give up and rewrite it from scratch. When you prioritize the now over the future, you can create technical debt. Try to avoid technical debt whenever you can. At least be aware of how your code might be problematic in the future and how you should try to avoid bad practices. Sometimes, when I write code, I think “this is a bad way to do it, but it still gets the job done.” I shouldn’t do it, but I do it anyway on occasion. Nobody’s perfect, but try to avoid bad habits.

P vs. NP – is it easy to make something that’s easy to verify but hard to solve? Sudoku, for example. Encryption too. When big O notation gets brought up, it’s usually referring to how long it takes to run something. The amount of time it takes to run something is called its time complexity. There are different classifications of computational complexity. This is a sub-field of computer science called Complexity Theory. You can study it for advanced degrees, but not in undergrad. P means Polynomial time, and NP means Nondeterministic Polynomial time. Basically, P is easy problems, and NP is hard problems. The question is: are these things the same? If it turns out that all problems are easy to solve, then encryption is easy to crack, and everything is hackable.

If you can solve the P vs. NP problem, you will be awarded a million dollars and given an award from the Clay Mathematics Institute. But then again, if you did solve it, you could probably make more money by selling it to an information security organization, though that might be a less ethical option because they might use it for nefarious things. But what I’m trying to say is that I think this knowledge is worth way more than a million dollars.

It’s a tough and important problem in computer science. It’s considered to be one of the hardest unsolved math problems in the world. If P = NP, then information security is pretty much mathematically impossible. If P is not equal to NP, then security is possible. Of course, even if P =/= NP, people can still make security mistakes. It doesn’t guarantee security. Similarly, even if P = NP, it doesn’t guarantee that people will figure out how to hack things, just that they are potentially hackable.

Computability theory – the sub-field of computer science that is concerned with figuring out what can or cannot be computed. Research in computability tries to figure out the limits of computation.

Halting problem – an unsolved and possibly unsolvable problem in computer science: will a program have a definite halting point (when it finishes running), or will it run indefinitely?

Formal verification – the process of formally testing something to see if it’s correct or not. This topic comes up with security quite a lot. Some people say something is formally verified to have correct results, meaning it’s hack-proof, but I have yet to see something that was 100% hack-proof and I sort of doubt that there ever will be something like that, formal proofs or not.

Combinatorics – the study of combinations and sub-sets within finite sets. Combinatorics is a math field that comes up in computer science a lot. We use combinatorics subconsciously for all sorts of things without even thinking of it as combinatorics. If you have letter magnets on your fridge, or you’re playing Scrabble, you’re using combinatorics. Another example would be combinations of pizza toppings. There are many different pizza toppings, but you don’t have to use all of them.

Examples of combinatorial problems in computer science include the knapsack and traveling salesman problems. Something that pertains to combinatorics is said to be combinatorial, such as combinatorial algorithms or combinatorial optimization.

There is a Ph.D. specialization for computer science called Algorithms, Combinatorics, and Optimization, or ACO for short.

Out of the box – stuff that works with minimal configuration. Many software solutions, like frameworks, are not out of the box. You can use them in your code, but you’ll have to do stuff to make them work. An example of an out-of-the-box project is pfSense, which a router operating system based on FreeBSD, which lets you have a powerful pf firewall without having to set it up yourself.

FFmpeg – if you want your program to edit videos in some way, use FFmpeg. Many video converter programs are just front-ends for FFmpeg.

ImageMagick – a program for editing images. If your website takes user-uploaded images, like for profile pictures or posts, you should use ImageMagick in your app.

Legacy code – old code that is still around. Legacy stuff might be hard to maintain, out of date, or require old dependencies. It might be on its way out, but you need to keep it around just a little longer. Legacy code can be hard to fix or secure. When you are working at a company as a software developer, you’re not going to start from scratch. You’re going to be working on their in-house infrastructure, meaning you’re working on code other people have already made. Not all production code is legacy code. But legacy code is code that is showing signs of aging.

Pair programming – pair programming is when two people are working together to write software, but there’s a catch: they’re only using one computer, and only one person is using the keyboard and mouse, but they’re not allowed to come up with the code to be typed. The person who is not at the keyboard tells the other person what to write. They can switch roles eventually, though.

Some people claim that this is useful for team-building because there is more communication between software developers. It also gets people to understand things better because they have to explain why they’re telling someone else to type it. This is similar to the Feynman Technique of learning, which can be summed up as learning by teaching. The person who writes the code on the keyboard can ask why the person is telling them to do something a certain way. It can be back and forth. All in all, it’s more social and also requires you to be more conscious of how you’re writing code.

Code rot – over time, certain tools or libraries will be abandoned by their developers. Eventually, their websites or repositories where you can download them might be gone. In that case, you might run into a situation where you can no longer recreate your toolchain that you used to create and modify a particular software project. Maybe you only have the compiled executables for a project but lost the source code files. When you can’t maintain or use code anymore, it’s called code rot.

Problems with dependencies – if you depend on external projects, such as a framework, library, or program (like a DBMS), if you use a lesser-known one, then there’s a risk that its development will be stopped and/or it will be shut down. JS frameworks come and go all the time.

What happens to developers who were using something after it get abandoned? Well, if someone finds a security issue in it, it will never get fixed. So developers who relied on it have to migrate their project to an alternative. People love to hate on popular programming languages, libraries, DBMSes, etc. but obscure ones are more likely to cease development and leave you with no support. The big ones have been around for a while and will continue to be around for a while.

Toolchain – the complete set of tools you need to make your software. It can consist of a programming language, library, API, IDE, debugger, compiler, version control system, terminal emulator and shell, unit testing framework, continuous integration, automatic build tools, a remote compiler server, and more. It’s good to keep track of what is necessary to make your software. Maybe your computer will get lost, stolen, or need repairs. You will want to be able to git clone your repo to use it on another machine. Maybe your computer is old, and you want to get a new one. In that case, you need to be aware of your entire toolchain that is required for working on your project. It doesn’t just mean keeping track of the programs, but also the configurations for them too.

I’ve run into situations where I had trouble getting back into working on some of my ancient Java software because I didn’t properly keep track of the toolchain I was using. There were certain things I used in older versions of Java that were removed in newer versions, and specific dependencies had to be certain versions too. But back then, I didn’t keep track of toolchains.

If you’re coding, you might use a code library, and let’s make up an example called SomeLibrary. You will find that, sometimes, you can’t just use SomeLibrary for your code. You might have to be sure to use SomeLibrary v1.3.27. If you update to v1.3.28 or newer, it might break your program because they changed how the library works, and you won’t be able to run it anymore. But the problem is that there might be a security or stability issue in v1.3.27, so you need to change your code so that it runs with the newer versions. It’s more complicated than just pressing a button and having everything update and get fixed automatically.

People who design programming languages and libraries have very difficult decisions to make. If they get rid of bad features, they break compatibility with code written by people who made use of those features. But if they keep them in, new developers might make the mistake of using the bad stuff. What’s more important: backward compatibility for existing projects that use it, or making things better for the future? It’s a tough call.

Big Endian – storing bytes from highest to lowest. Most people are used to numbers that go from the most significant bytes to least significant.

For example, in the decimal number 5,283, you have 5 thousands, 2 hundreds, 8 tens, and 3 ones. The values of the digits are biggest on the left and get smaller when going to the right.


Binary place values are all multiples of 2 (1, 2, 4, 8, 16, 32), whereas decimal place values are all multiples of 10 (1, 10, 100, 1000, 10000, 100000). If you’re wondering why there’s a ones place, remember that any number to the zeroth power is 1.

In Big Endian, 5283 would be 0001010010100011. The decimal number 5283 requires 2 bytes, which is 16 bits. The two separate bytes in the binary representation of 5283 are 00010100 and 10100011.

Endian is byte order. Big Endian means the most significant bytes first, then the smallest.

Little Endian – the opposite of what people are used to. Values go from smallest to largest (for whole bytes, not individual bits). Because 5283 is 0001010010100011 in binary, it’s 00010100 10100011 in Big Endian, but 10100011 00010100 in Little Endian. This means the first byte is the smallest, and the next one is bigger. But in each individual byte, the bits still go from biggest to smallest.

CFP – Call For Papers. For an event, such as a security conference/convention, a CFP is when a group that is in charge of hosting an event asks for people to submit their papers, which are their ideas about security concepts that they can present during the events.

ICS – Industrial Control System. Computers aren’t just laptops and tablets. ICS equipment runs on computers, too.

SCADA – Supervisory Control And Data Acquisition.

SCADA and ICS technology is used in industrial settings, such as for critical infrastructure. As such SCADA/ICS security and stability are very important.

Critical infrastructure – power grids, communication lines, water supplies, and transportation systems are examples of critical infrastructure. A country can’t function without its critical infrastructure. Modern critical infrastructure systems involve computers and software, and as such, many people are concerned about their security, and how critical infrastructure hacks can potentially destabilize society.

Neural network – A neural network can be used for things like image recognition, generating images, detecting malware, and more. One popular way to create a neural network is with the software called TensorFlow, which is free and open source. You can use TensorFlow with Python to create your own neural networks.

TensorFlow was only created a few years ago, so don’t feel bad if you’re new. A lot of technology is so new that nobody has much experience with it.

A neural network has input data that it can “learn” on, and layers for figuring out how to classify something. The end result will be trying to classify something. For example, trying to figure out what objects are in a photo, or what handwriting says in terms of digital ASCII characters.

For learning, you have a neural network “train” on a lot of data. Neural networks can make random changes in how their in-between layers for classification work. Then, whichever one is the most accurate moves on and can be changed and improved in the next iteration.

Neural networks involve random changes, assessing how accurate the results were with multiple layers of decision-making neurons, and then taking the best variation and moving on to repeat the process all over again. To train a neural network to detect images of cats vs. dogs, I have to have many images of cats and dogs, and I need to classify them as one or another. But the neural network doesn’t know which is which. The neural network tries to classify them, and it might get a lot of them wrong. You can compare what the answers are to what the neural network gets.

You need to train it on training data so that it will be able to go out in the real world and try things on its own. Without training data, anything else would be hard to achieve.

It’s making randomly-generated neurons (why it’s called a neural network) and then giving them a test, assessing the results, then only moving on the best ones to the next round, but making new neurons based off of the most accurate ones — survival of the fittest.

It’s a trial-and-error process. Having more iterations and more data gives more accurate results. That’s why companies like Google can do well with them – they have tons of servers and massive amounts of data.

Each classification node is called a neuron, but the reason why it’s a network is that you have your inputs and then multiple layers of interconnected neurons, eventually leading to one of a finite number of final classifications.

Below is a visual representation of what a neural network is like. There are inputs, hidden layers, and an output layer. The input into a neural network might be image files, such as JPEGs. The output layer might be classifications for dogs, cats, or neither. The goal of a neural network is to take an input and then put it into the correct classification.

Don’t bother with trying neural network stuff until you’ve done some simpler coding projects first.

Stack trace – when something goes wrong, a program can crash, and then it might show you some information. To someone who isn’t a developer, the output of a crash might look like random gibberish. But it’s a stack trace, and it tries to help you understand what went wrong. If you’re the developer of the program, it will provide information that will be useful to you. If you’re just a user of a program and see a stack trace, you can submit a bug ticket with the information it contains. Just be aware that a stack trace could potentially have personal information in it.

For a web app, a stack trace can help a hacker learn more about the software running on the server, so it’s good to make sure they aren’t seen by users of the site, even if something goes wrong.

Lexing and parsing – in plain English, lexing and parsing is how a computer understands code. When you look at text on a page, your brain realizes that a bunch of letters next to each other make up a word, based on rules of language that you know. Then, you use your knowledge of grammar to figure out what the words mean together in a sentence. It’s similar for code. You might be familiar with the word lexicon, which has the same root as lexing. Lexicon means the words you know. Lexing is like that, but for computers.

Lexing involves turning characters into tokens (a process called tokenization), which is like using your brain to turn sequences of letters into words. Tokens are things like delimiters, keywords, or identifiers. It turns characters into things a computer can understand better. There is another definition of tokenization that is related to information security, but that’s not related to lexing or parsing. Parsing is when tokens are converted into a parse tree, which the computer uses to finally make sense of it.

Parsing is also called syntactic analysis. In order for a compiler to compile code, it first needs to perform lexical and syntactic analysis, and then it can perform the compilation. Keep in mind that, in this context, parsing refers to code parsing in the process of compilation. It’s not quite the same as parsing a file, such as JSON or XML file parsing.

Don’t shoehorn concepts!

Just because you learn certain coding features doesn’t mean you need to put it into all of your software. An earlier chapter introduced object-oriented programming, but sometimes, you don’t need classes or objects. You can just write a simple procedural program that does what you need. There are many situations where I think developers over-engineer their code because it makes them look smarter.

For learning, a new personal project might introduce stuff, such as a new library or language, but that’s for the sake of learning to expand your skill set (but it’s not advisable to do that at work – only good for personal projects). In the book Software Developer Life by David Xiang, he mentioned how someone he was working with put Redis (a tool used for database caching to speed things up) into a project so he could get experience with it for writing a book about Redis. But it introduced unnecessary complexity into their company’s bottom line, making development more time-consuming and complex, which was really bad. Don’t get me wrong – database caching has some definite advantages. But when you don’t need something, and just add it into your project for the sake of using it, then you’re adding additional complexity and creating more work (and headaches) for yourself and your team.

Nowadays, there are lots of projects that mention blockchain, cloud, or AI, when they simply don’t need them. Companies see buzzwords as a way to get people interested in something, but at the end of the day, many programs can get by without all sorts of cool-sounding concepts being forced into them.

I don’t remember who said this, but I remember someone saying something like “a bad engineer prefers a complex design because they think it makes them look smarter, but a good engineer prefers a simple solution because it’s more efficient.”

Think of it like this: when you cook food, do you use a kitchen knife or a Swiss army knife? A kitchen knife, of course. It’s simple and very good at what it needs to do (cut up food so you can cook it). A Swiss army knife has many more features, like a corkscrew, bottle opener, file, and more. But if all you need is a knife, then having all those extra features doesn’t help at all, and can actually just get in the way.

If you feel the need to add classes and generics and overkill exception handling just for a very simple program, that’s a waste of time, especially if you already know the concepts. Use the right tool for the job, and don’t add extra stuff.

Congratulations on finishing section 14! You’re almost done with this website’s content!

Assuming you’ve completed sections 1-13, you’ve accomplished quite a lot so far. But there are only a few more sections left.

It’s okay if you’re skipping around too instead of reading them all in order too. Do whatever works for you.

← Previous | Next →

Advanced/Miscellaneous Topic List

Topic List

Leave a Reply

Your email address will not be published. Required fields are marked *