Conventional programming advice holds that it does not matter which language you choose to get started in so long as you start to learn programming. Like most advice for regular people this is watered down wisdom distilled into a short heuristic that gets circulated once it hits a critical mass and then gets accepted as correct. I’m here to tell you that advice is terrible. There are excellent languages choices to start with that will supercharge your learning and we are going to detail why that is the case.
A history of programming languages
First off, what is a programming language? Wikipedia states “A programming language is any set of rules that converts strings… to various kinds of machine code output.” That definition is broad, but suitable given the history of the craft and the current state of things. Programs are written to manipulate hardware to perform the proper mathematical and data/memory operations to produce a desired result.
Starting at the beginning of programming, CPUs were coded directly with strings of bits referring to operation codes shown in the graph below. In the case of JMP below, this is 0100111011111001, or 4EF9 in hex1. This is ultimately how your PC understands and processes information since CPUs are binary driven.
Assembly allows for a level of convenience and abstraction to this. Using the mnemonic on the table above, the same command in binary can be written as jmp ‘address’, and now you don’t need to write direct binary saving your poor eyes. Indeed, Richard Hamming even states how crazy is was to write direct binary for computers
I once spent a full year, with the help of a lady programmer from Bell Telephone Laboratories, on one big problem coding in absolute binary for the IBM 701, which used all the 32K registers then available. After that experience I vowed never again would I ask anyone to do such labor. Having heard about a symbolic system from Poughkeepsie, IBM, I asked her to send for it and to use it on the next problem, which she did. As I expected, she reported it was much easier. - Richard Hamming, The Art of Doing Science and Engineering
There is now a human readable language that allows you to program the computer that isn’t binary thanks to assembly. We now have achieved one level of translation (or abstraction) between binary and assembly. If you understand patterns, you can now see where this eventual leads. We now have many languages so far removed from binary code that you would easily forget that programming is for CPUs. The rest is well understood history, but FORTRAN came along and revolutionized programming again in 1954. If you look at the syntax below2, you can see vestiges of the original “high level” programming language.
C AREA OF A TRIANGLE - HERON'S FORMULA
C INPUT - CARD READER UNIT 5, INTEGER INPUT
C OUTPUT -
C INTEGER VARIABLES START WITH I,J,K,L,M OR N
READ(5,501) IA,IB,IC
501 FORMAT(3I5)
IF (IA) 701, 777, 701
701 IF (IB) 702, 777, 702
702 IF (IC) 703, 777, 703
777 STOP 1
703 S = (IA + IB + IC) / 2.0
AREA = SQRT( S * (S - IA) * (S - IB) * (S - IC) )
WRITE(6,801) IA,IB,IC,AREA
801 FORMAT(4H A= ,I5,5H B= ,I5,5H C= ,I5,8H AREA= ,F10.2,
$13H SQUARE UNITS)
STOP
END
From this point on programming productivity jumped significantly with many languages developed in the 60s and onward that are still used significantly such as Lisp and C. These days it is as simple as launching a Linux distro and entering ‘python3’ and ‘print(“hello world”)’ into the interpreter to complete your first program.
Which Language to Choose
Now that we understand the history of programming language, is it smart to say “it does not matter which language you choose to start with?” Of course not, no one willingly writes assembly or binary code since it’s slow, inefficient, and hard to understand. These three points are exactly why you should choose a good programming language to start with. The last thing you want to do when learning programming is to fight the syntax, the spacing, the installation tools, the inherit flaws of the language, or ourselves. Programming can be frustrating even for people who work in it everyday, beginners need the least amount of friction to ensure they do not get discouraged. This is why I will be adamant when people say pick any language, that it should be a language with good documentation, easy to use, a history of troubleshooting documents, and large support behind it.
Let us dive into a few common recommendations and understand the nuances of each.
JavaScript
JavaScript is almost always a universal first recommendation since it is a fundamental technology that powers the web. If you’re reading this you have a JavaScript interpreter in your hands already in your web browser, just hit F12 with your browser open. It makes sense to recommend JS, its syntax is fairly straight forward, you can build fun, interactive GUI’s thanks to the inherit attachment to HTML and CSS and web sites. It now is also a very popular backend programming language thanks to the rise of NodeJs. It also has tons of libraries such as React and Tensorflow available to tackle problems you might be interested in.
If I had to rate this advice, it is purely average (6/10). JavaScript and its ecosystem have a significant amount of problems that would frustrate any beginning user.
You need to make a decision between Browser based JS or NodeJS based JS.
A user shouldn’t have to make this choice off the start. If you choose browser based JS, you then also need to learn basic HTML to load your JS, and you also need to eventually learn to run a web server to do anything more advanced.
If you choose Node, you first need to deal with an install process that has foot guns in the future (please use nvm-windows for windows, and nvm on Linux). You can also rapidly approach the fundamental problem of JavaScript not having a built in core library and get sucked into NPM package hell. More in this in another point.
Additionally, JS was developed as a browser first language, so it can be unclear to a new user that many of those browser APIs are not available to them. The same can be said for the other direction also. Having two sets of documentation for development is not great for users (see python2 vs python3).
Browser compatibility is still an issue
JavaScript implementation is dependent on the browser, and not all browsers support every feature. Internet Explorer is the biggest criminal towards this, but even modern browser API’s are not 100% compatible between Chromium, Firefox, and Safari. You’ll need to frequent Can I Use to determine if your API is compatible.
Package dependencies are hell
Node Packages are one of the worst offenders out there from a library standpoint. Anytime something non-trivial needs to be written in JS, the age old solution is to install a package. I can not blame people for this, JS has terrible core functionality since it 1) needs to be shipped over the internet, so it needs to be small, 2) Browsers can’t even agree on functionality. The result is any non trivial application in JavaScript will require third party libraries.
But the result of this is needing to install hundreds of megabytes locally to even make a NodeJs application. Web Site’s used to just import libraries directly with <script> tabs, but now many popular libraries require a preprocessing and compliation phase to ship. For example, Angular has its own syntax that is unique to itself and requires preprocessing before any deliverables can be hosted. This isn’t user friendly to learn a highly specific subset of a language to do something useful. The days of writing pure JavaScript are over sadly. If you want to build a Single Page Application with Routing, be prepared to install many packages and learn a completely new framework.
This also doesn’t include the massive security risks that happen with NPM hosted packages, often stealing credentials or mining bitcoin under your nose. Even the left-pad nonsense caused tens thousands of applications to break because people were too lazy to write a few lines of code.
The Fundamental design of the language is silly
I can’t do this portion justice as the perfect video covering the silliness of JavaScript is encapsulated here:
TypeScript is superior
Microsoft’s homemade language superset TypeScript provides incredibly useful static analysis and typing to a language known for its dynamic typing. The experience has allowed for creation of superior web code with the drawback of now requiring compilation for all browser based deployments. This is great for new users but also forces new users to immediately adapt to two languages, plus needing to implement ts-node into your build process instead of deploying right away. Again, the goal is for new users to pick a low friction language and while Typescript is the right choice is the long run, it adds initial complexity to things since it requires NodeJs in some shape to process the code first, or on the fly. Anders Hejlsberg also designed the language who was previously known for developing C#’s language (see a pattern here?)
JavaScript is fundamentally Asynchronous
This is a good and bad thing, but for new users it can be confusing to work with things like Promises and closures since it requires you to think of the execution timeline of your code. I would almost always recommend that users avoid asynchronous code to start with (though JavaScript is the perfect introduction to async code due to it being single threaded). A great introduction on the event loop is linked here.
It’s not all doom and gloom
Now it is not fair to completely dump on JavaScript, after all the web is effectively powered by it and it kept me employed for a long time. But there are many fundamental issues that new users can run into that makes it not a simple choice as it is made out to be.
If you are looking to get into UI design, there is not a more useful language out there for that right now. HTML + CSS + JavaScript can be run on almost any device in the world with which a browser exists on. You can write one application and deploy the same code to mobile, tablet and PC. The resources out there are immense and you almost never need to reinvent the wheel.
JavaScript has also been heavily modernized in the last few years with functional programming and pure functions coming into fruition. Things like map(), filter(), reduce() are a pleasure to work with as they make data manipulation fun to work supercharge your work.
JavaScript is also a web first language, meaning all the tools to need in that domain are provided to you at low cost. You can easily make Fetch/XMLHttpRequests to API’s to power some custom site and set the proper headers to ensure everything works right out of the box. Websocket support has also come to almost every browser, allowing for rich live applications that update automatically with the latest data.
Python
I’m not going to spend too much time on Python since my knowledge is a bit out of date, but it’s hard not to recommend Python. It is a pleasant language to work in. Their documentation is excellent, it has a long history of troubleshooting on platforms like stack overflow, and it is one of the most popular scripting languages in the world. Their core library is really strong and allows for powerful data manipulation right away. Their third party library system is also fairly good with pip and due to its connection to C, most important libraries in that ecosystem have Python based bindings also making it super simple to use things like OpenCV. Chances are any area you are interested has significant python support (web3, ML, Imagine Processing, Big Math, etc..)
There were some issues on the upgrade from python2 to 3, but we are far enough beyond those issues that to confuse the two would be difficult for most new users even if they run across some old documentation.
That said, Python’s strengths are as scripting language due to it also being a dynamic and interpreted language. It has an extremely low barrier to install (normally installed by default on most Linux distros) and the code is extremely portable due to being interpreted. Virtual Environments also make it easy to mange multiple projects and not worry about differing dependencies or polluting the environment variables used to run the project.
There are some downsides however, and I always felt that debugging for python was a second class citizen in terms of UX. pdb() is useful but it always made debugging a chore rather than just a tool used as part of the process. Since Python is so dynamic, is it necessary to launch the debugger frequently to determine what objects we are working with at times and that can result is some significant overhead. I’m sure the workflow has improved since I last seriously used it but the Linux and command line focus of Python makes it a harder barrier for entry.
C/C++, or any other lower level language
I would absolutely recommend first time programmers avoid any programs that force you manage memory manually. There are too many easy issues with C and also C++ that result in unclear errors and frustrating experiences. It almost seems to be a right of passage with C to have your first Segmentation Fault because you made an array and accessed it out of bounds. Your goal as a new developer is enjoy working with the language, not fight it.
Learning Pointers are a useful concept to understand once you are at an intermediate point but C’s aggressive use of them can be difficult as to whether you are working with data or an address to the data. It also makes the read ability of the code much worse, which makes code samples harder to study. The tooling is also old and not user friendly compared to modern conveniences that languages like C# offer. Your choice of compiler also determines what code you are capable of writing, such as gcc, llvm, msvc, and others. Intermediate and Advanced C code verges on the territory of witchcraft, with things like undefined behavior becoming normal usage. You also need to deal with decades of API changes, where the built in string copy function strcpy() has resulted in security bugs3, and well the proper way to copy is string is somehow multiple stackoverflow responses.
If you must pick a low level language to start with, then I would highly recommend Rust, a modern creation that is safe by design. The tooling is also modernized and user friendly and Cargo is a really neat package management system.
So Why C#?
If we recount the main complaints I’ve had with most languages, it falls under these categories:
Bad first and third party library support
Poor documentation
Historical baggage / Bad language design
Tooling is a Bad User Experience
Bad Install/ Bad Ease of Running
Evaluating this list, and based upon the current state of C# with .NET 6 launching within the last year, I can confidently say it is one of the best languages to code anything from small apps to enterprise level code. It is extremely user friendly thanks to a cross platform, OS based runtime support compared to the previous Windows .NET Framework approach.
History of C#
C# was designed as a Microsoft’s competitor to Java and also a next generation of platform code for its Windows system along with .NET framework, which is an ecosystem of libraries and software that all run under the same Common Language Runtime. You’ll often hear C# and .NET bundled together since they were effectively built to work together and C# was the most compelling value add for the CLR, though the CLR is language independent. Visual Basic (~2002), F# (~2005), and Microsoft Visual C++ (~1999) are also supported which allowed for a lot of flexibility when building software at a time when computers were really becoming mainstream in 2000.
If this sounds confusing, don’t worry. Microsoft and .NET framework have a lot of baggage over the years that rightfully earned them criticism. Many hardcore free software advocates and Linux users hate Microsoft for their anti competitiveness over the years, but I think it is finally safe to say the Microsoft has embraced Linux and Open Source and there is minimal threat now that C# is open source and .NET is run by a foundation.
Thankfully .NET Framework is no longer needed to write apps. Microsoft shocked the world in 2014, announcing an open source of .NET along with cross platform capabilities in Dotnet Core. The end result of that 8 year journey is .NET 6, the first long term supported, multi platform, open source, multi architecture, and multi device all encompassing software library and runtime. This has freed C# from the baggage of .NET Framework and Windows specific code and is now perfect for all new comers to learn. Where .NET Framework required Operating System updates to get new features and patches, Dotnet Core ships in self-contained runtimes that are one click installs right from Microsoft’s site and allow for multi installations and targeting. Dotnet can also completely be used via the command line if desired for more Linux style operation, but also has rich integrations with IDEs for more graphical users.
Let dive in why I think Dotnet/C# is a great language to get started in.
Library Support is second to none
What .NET 6 means is that users can now build software for a range of devices, from Phones, to Tablets, to Desktop, to Web, to IoT, and to Cloud, in a variety of architectures such as x86-64 and arm64 devices, and in a variety of operating systems. This adds significant value for all developers and companies as C# now has immense flexibility to program anything you need with the library support to back it up.
When I suggest C# as a great language, the ecosystem of first party libraries are an incredible achievement from Microsoft. Their CEO along with the most talented software engineering devs at Microsoft have backed this initiative and the tides will be tough to ever turn back.
Many third party libraries are also excellent due to the popularity of the language. Nuget packages are just .zip files that contain the .dlls of the library needed and are generally very simple to consume either via an IDE or the command line. Since there was a significant split between .Net Framework and Dotnet Core, .Net Standard targeting was created to allow libraries to be consumed by either style project. This allows for your favorite packages to operate in old and new projects with the same APIs.
Some libraries I would recommend:
Serilog: An enhanced logger with significant customization options
Entity Framework Core: A ORM for your data classes
Moq: A powerful testing library used to mock dependencies
XUnit: A strong unit testing framework for users
MediatR: An in memory messaging framework
Automapper: reduces overhead in class data conversation
Official Dotnet Docker images
For a good example project that contains many of these libraries, check out Clean Architecture.
Documentation or Die
The benefit of .NET/C# starting out as a private and centralized effort is that documentation had to be made available to end users in order to gain traction for adoption. Once the open source bug hit Microsoft, they also willingly updated their documentation to follow suit. They have supporting docs for almost every type of project you would want to implement along with sample code to get you started. Often their documentation is written by experts in the technology and is priority for them to update.
For beginners they have a Tutorial page and an Introduction to .Net. They also contain more advanced guides on how to implement project software rigor in your project, such as their Architecture Guides. In particular their Microservice Guide and Web Application guides are great as you could follow them to the letter and get a working application out as a result. Importantly they also include sections for Security, Testing, DevOps, and all other major concerns besides just pure programming.
All this effort points towards C# being perfect for beginners as you will almost never lack for information. When you do run into issues however Stack Overflow has significant history with C#, with power developer Jon Skeet answering almost 20k of questions over the years. Chances are Jon (or Eric Lippert) have answered a question you are going to ask, and the answer will be high quality. I highly recommend his C# In Depth book, which is basically a bible on the language.
Old Dog, New Tricks
Almost all languages suffer from earlier design decisions. Languages themselves are also software, and the usefulness of the languages relies on its features and syntax.

C# had a few year delayed start on Java and was able to learn from the mistakes of their competitor (though initially .Net was designed also support Java concepts) and frankly delivers more useful languages features at a faster rate than they do. For example, Generic support in C# is generally seen as superior since they are baked into the language themselves and have received numerous updates with type constraints. Java supports generics but due to type erasure, you can run into some issues that are unexpected for a new user. But don’t take it from me, even Jon Skeet agrees:
Syntactic Sugar has also been a priority for the language. No longer are the days where you need to specify the type of a variable type, the “var” keyword now exists.
int numberOfBooks = 5; // Old Style
var numberOfBooks = 5; // implicitly typed as an int
var numberOfBooks = 5L; // implicitly typed as a long
The other side of this is now the “new” keyword can be used to cut down on the verbosity of declaring generic lists. This cuts down effort significantly when creating large nested classes or generics, and your local IDE can be configured to warn on those style violations, ensuring code is consistent on your team.
List<long> bookIds = new List<long>(); // Old Style
List<long> bookIds = new(); // New Style
var bookIds = new List<long>(); // var style
List<long> bookIds = new() // New Style with initial elements
{
1,
2,
3
};
A highlight list of fun features C# has added follows:
Raw String Literals (C#11)
Switch Expressions (C# 8)
Null Coalescing assignment (C# 8)
Records (C#9)
Top Level Statements (C# 9)
Nullable Reference Types by default
LINQ is also a significant feature for C# that I use almost every day. LINQ allows for a SQL like data processing operations on generic enumerable types (lists, sets, dictionaries, etc…). These allow you to powerfully transform data into new format with things like select, where, groupby, orderby, max, min, all, any and more. Since everything is statically typed the compiler will also help you with nasty and complex queries, especially with nested objects. See the example below, which runs as is thanks to Dotnet 6’s Top Level Statements:
var people = new List<Person>
{
new()
{
Name = "Bob",
Location = "London",
Salary = 100000
},
new()
{
Name = "Ozzy",
Location = "Birmingham",
Salary = 50000
},
new()
{
Name = "Bill",
Location = "London",
Salary = 75000
},
};
var peopleInLondon = people
.Where(p => p.Location == "London")
.Select(p => p.Name);
var averageSalary = people
.Average(p => p.Salary);
var salariesGreaterThan200K = people
.All(p => p.Salary > 200000);
Console.WriteLine($"Average Salary: {averageSalary}");
Console.WriteLine("People in London: " + string.Join(", ",peopleInLondon));
Console.WriteLine($"Are there Salaries > 200k? {salariesGreaterThan200K}");
public class Person
{
public string Name { get; set; }
public string Location { get; set; }
public long Salary { get; set; }
}
// Output:
// People in London: Bob, Bill
// Average Salary: 75000
// Are there Salaries > 200k? False
Extension methods are also useful in that they allow you to add methods to a type that don’t exist natively or are not open for extension. This lets you to easily enhance a type with custom logic for your application without polluting the global namespace of the object. LINQ is written purely through Extension methods as that allows for Generic Enumerables to not have massive amounts of methods right away unless desired. You can see a complete example here.
Many people deride C# for its static typing (dynamic types do exist!), but their static code analysis is one of the most helpful tools I’ve used to avoid obvious code traps and smells. The reality is we are not smarter than the compiler, so if it can help us write better code, we should use it! With .NET 5 the Code Analyzers come with the SDK, meaning you don’t need an external setup to get optimal feedback on your code. This couldn’t be more perfect for beginners, since .NET Framework required you to install things like FxCop and configure it correctly before starting.
Microsoft’s main web library was ASP.Net and it often received many complaints due to the fundamental design being extremely hard to test. ASP.NET was designed in a time that made it difficult to do any sort of integration tests since monolith code and QA Testers reigned supreme. The successor library in ASP.NET Core was designed from the start to fully support Dependency Injection in all facets. This influenced all applications that used the library to also hook into the DI system, allowing for easy unit tests when combined with something like Moq. Additionally Microsoft made a TestServer class that allowed for easy in memory tests without needing a live Kestral server configured locally. These testing approaches made it stupid simple for users to create a well tested and rigorous application right away.
Cloud Support
Azure obviously has first class support for Dotnet and C# being Microsoft’s own bread and butter, but AWS has also started to offer large support for C# based implementations. With Dotnet 6 being the first unified Long Term Supported implementation it makes sense to build things like Lambda’s with C# as you know you have a 3 year window of support with them. Amazon has done their best to take advantage of Dotnet 6’s features and adding their own utilities, such as improved logging, executable assemblies, minimal APIs for stupid simple one file APIs, and more performant runtime. Cloud support will start to become a critical factor in the viability of a language going forward as software continues to shift to that realm.
Devil’s Advocate
This is not to say that C# doesn’t have its flaws, because it certainly does. Most drawbacks were due to the kitchen sink of .NET Framework, some poor library design such as the massive System.Web and all its configuration parameters, the intense coupling of Windows and .NET resulting in poor testability. Main feature libraries such as ASP.NET are now an ancient beasts running on old IIS servers. Microsoft also has big business ideas that ultimately get killed (Silverlight) or don’t pan out (UWP), and Developers bear the brunt of these short sided decisions. It is totally fair to criticize Microsoft for having too many naming conventions and products also, their eco system is so large and constantly changing it is really hard to stay on top of things.
Language wise, due to C#’s static typing and object oriented manner, the language is still somewhat verbose compared. For example, many mathematical operations are just static methods on the Math class. However this can be simplified with a “using static” declaration. C# also relies heavily on reflection at times which is their version of metaprogramming. It allows for some powerful code handling assemblies and types allows but can be confusing for newer users. Dates and Times were notoriously a pain to work with, with Dotnet6 only recently adding the DateOnly struct.
Additionally, there is no true cross platform GUI library. There are attempts with 3rd party Avalonia, and .NET’s MAUI targets almost everything you need… except Linux based OSes. However, web based applications are fully supported and Blazor allows you to use C# over JavaScript!
With having to support docs for Dotnet Core 1, 2, 3, 5, 6, 7, and .NET Framework, it easy to land on the wrong page or find sparse information on a topic, especially the further back you go. Often .NET Framework documentation will pop up, only for you to realize you need the Dotnet Core version and that does not exist (E.g. Windows Communication Foundation), or a feature only exists it a newer version of Dotnet Core. It is also common to run into an API you want to use, but there is no documentation code available, so you’ll need to hope there is a blog post made linked elsewhere that show you how to use it.
The biggest flaw for C# is the lack of cross platform IDE for users. Visual Studio, Microsoft main IDE, doesn’t support Mac or Linux. As an alternative for other OSes, JetBrains Rider is cross platform and now my preferred IDE, however it is not free. VS Code, while amazing and cross planform, has just average support due to it being language server driven analysis, compared to Roslyn based analysis which at the end of the day actually compiles the code. I would recommend trying out JetBrains and use their student discount if you are seriously interested, but VS Code can suffice also for small to medium projects. For any serious level C# development, especially enterprise, a full featured IDE is critical.
I hope this guide helps newcomers and existing programmer consider the switch to C#, a language I have loved for years now that I only see getting better. Thank you for tuning in!
More on 68000 JMP here: https://mrjester.hapisan.com/04_MC68/Sect05Part02/Index.html
Code Sample courtesy of Wikibooks: https://en.wikibooks.org/wiki/Fortran/Fortran_examples#Simple_Fortran_II_program
See Buffer Overflow, Section 4.3.2: https://web.ecs.syr.edu/~wedu/seed/Book/book_sample_buffer.pdf
Thanks for this! I’m jumping into C# this year, and I’m finding the Microsoft Learn platform to be fantastic.