• 0

How do you comment?


Question

I know that commenting is a huge debate in programming. I was reading this article on Ars Technica, which is not primarily about commenting, but noticed that the person asking the question noted that he doesn't comment his code because he believes it is self-evident. However, most professional software development firms require commenting of some sort. So what is your personal opinion on commenting your code? Do you comment a lot, not at all, or somewhere inbetween? And, most importantly, why do you take that position?

As I have become a more experienced developer I have developed my own style. By most programmer's standards I believe that I comment quite a bit. I have a couple snipets of my code below, with the full source code for the class attached. (Note: The spacing is slightly screwed up. Not my fault. I blame Neowin's post editor.)


/*
xorangekiller's File System class for easy manipulation of objects on the file system.

This class is essentially a wrapper for Windows API functions.
However, unlike the file manipulation functions in the Windows API, all of these work recursively.

Windows file management functions: http://msdn.microsoft.com/en-us/library/aa364232.aspx
Windows directory management functions: http://msdn.microsoft.com/en-us/library/aa363950.aspx
*/
class xFileSystem
{
public:
// Initialization
xFileSystem();
virtual ~xFileSystem() {};
// Primary File System Utilities
bool CreateDir( const std::string File );
bool Copy( const std::string Source, const std::string Dest );
bool Move( const std::string Source, const std::string Dest );
bool Search( const std::string File, std::string Search = "", bool Case = true, bool Hidden = false, unsigned int Recurse = 0 );
bool Delete( const std::string File );
// Support File System Utilities
static bool Exists( const std::string File );
static bool IsFile( const std::string File );
static bool IsDir( const std::string File );
unsigned long long Size( const std::string File );
[/CODE]

[CODE]
/*
Search for files directories.

Callback:
Every time a matching file or directory is found, SearchCallback() is called.
If it is not overridden, it will save the found files to internal storage, which can be accessed by calling GetFoundFiles() after this function returns.

64-bit Note:
If this function is being run from a 32-bit program on 64-bit Windows you might need to call Wow64DisableWow64FsRedirection() first to allow searching 64-bit paths.
If you use the aforementioned function, don't forget to call Wow64RevertWow64FsRedirection() after it completes to revert the setting!
MSDN Documentation: http://msdn.microsoft.com/en-us/library/aa365743.aspx

Side Effects:
Be advised: If you are using the default implementation of SearchCallback(),
the vector GetFoundFiles() returns is reset (cleared) every time this function is called.

Arguments:
File [in] Directory path to search.
If you supply a file we WILL return with an error!
Search [in] [optional] Find files and directories matching this search string.
Wild characters '*' (any number of characters) and '?' (only one character) are supported.
If the parameter is left blank everything we find will be processed!
Case [in] [optional] Is the search string case sensitive?
This paramter is ignored if Search is "" or "*".
Hidden [in] [optional] Should the search include hidden files and directories?
Recurse [in] [optional] Depth to enumerate the search path.
0 Enumerate the entire path.
1 Enumerate only the base directory.
2 Enumerate the base directory and any directories it contains.
3+ Enumerate all directories up to the designated level.

Return Value:
true We found at least one file or directory matching the search parameters.
flase Nothing was found matching the search parameters.
Are you sure we have permission to access the path?
*/
bool xFileSystem::Search( const std::string File, std::string Search, bool Case, bool Hidden, unsigned int Recurse )
{
//
// Check for trouble.
//
if( !this->IsDir( File ) ) return false; // We can only search directories!
if( Search.empty() ) Search = "*"; // If the search string does not exist, search for all files.
this->FoundFiles.clear(); // Clear our found files vector.
//
// Search the source directory.
//
xDirIt Handle( File, true, Hidden, Recurse ); // Handle for iterating the directory tree.
while( Handle.It() )
{
if( this->Abort ) return false;
// Strip the full directory path.
const char * FileIt = Handle.FileName; // Pointer to the file name that we will iterate through.
const char * LastGood = Handle.FileName; // Last known valid directory path.
while( *FileIt )
{
LastGood = FileIt;
while( *FileIt && *FileIt != '\\' ) FileIt++;
if( *FileIt ) FileIt++; // We don't actually want the '\\' character itself.
};
// Pass on the file information if the file name matches our search criteria.
if( this->WildCmp( Search.c_str(), LastGood, Case ) ) this->SearchCallback( Handle.FileName, Handle.FileAttrib );
};
return true; // Assume that everything ran successfully.
}
[/CODE]

What is your opinion on my commenting? Which category do I fall into, from your perspective.

xfs_1_3_1.zip

Link to comment
https://www.neowin.net/forum/topic/1102831-how-do-you-comment/
Share on other sites

18 answers to this question

Recommended Posts

  • 0

You definitely fall into the "comment a lot" category, but as long as your keep your comments up to date, it's not necessarily a bad thing. It can prove problematic if your comments get out of sync with the actual code though.

Personally I think I sit somewhere in the middle. I believe that your comments should (for the mostpart) explain the "Why" in the code (i.e. why does this code do what it does) and let the code itself say WHAT it does. At the same time though, I think that people who insist on not commenting at all (the "all code should be self-documenting") are dangerous and make life hard for others.

For my code style, I'll generally do the following:

  • Comments at the top of the class, explaining it's purpose.
    /**
     * Unicode string class. Holds a string of unicode characters and provides methods for manipulating the string and conversion utilities.
     */
    class UnicodeString


  • Comments at the top of functions/methods explaining the method, the parameters, the return values, the exceptions returned, and any miscellaneous information relevant to users or maintainers.
    class AsciiString
    {
      /**
       *  Get a ROT-13 encrypted version of this string.
       *
       *  Parameters:
       *    iterations         The number of times to encrypt the string. Should be an odd number.
       *
       *  Return Value:
       *    The ROT-13 encrypted string. If the string is empty, an empty string will also be returned.
       *
       *  Exceptions:
       *    ArgumentException  The number of iterations provided was even
       *
       *  Remarks:
       *    - This method is useless. Seriously.
       */
      String GetRot13(int iterations = 1)
      {
        ..
      }
    }
    


  • TODO and HACK comments. In all my IDEs these are recognised as keywords in comments, and can be useful to refer to at a later date
    int SomeMethod()
    {
      // TODO: Complete this method.
      throw new NotImplementedException("This method is not currently available in the beta version of the API.");
    }
    
    PoorlyImplementedInteger GetSum(PoorlyImplementedInteger values[])
    {
      // HACK: The PoorlyImplementedInteger class does not provide operators to do basic addition, but it does provide comparison
      //       and increment so we can use a loop to do the addition. As soon as addition is implemented update this method to
      //       reflect.
      ...
    }
    


  • Explain the "why".
    string ParseMenuFromTelnetOutput(string commandString)
    {
      // Since telnet is simply just a character stream, we search for the menu input indicator ("$>") and then wait a little
      // longer in case more output is sent.
      ...
    }
    


Those examples are fairly generic, I'll often use documentation-generator syntax when applicable (in C++ I use Doxygen comments, in C# I use intellisense XML, in ruby I use rdoc, etc). I certainly try to avoid commenting every line, since it can simply end up patronising the reader, and confuse them too if the comments get out of sync with the code. Example:

// If index is zero, return false. <--- NO **** SHERLOCK!
if (0 == i) return false;

EDIT: Accidentally a code tag.

  • Like 2
  • 0

Maybe in my example function above my commenting is a bit extreme. I don't normally comment close to every line. (Actually, I didn't realize that function was like that until you pointed it out. There used to be more content there until I abstracted it into a directory iteration class.) I do, however, normally comment every interface (class, struct, function, etc.) and every variable I create. The other comments are optional: they're only there if I decided I need them. My older code tends to have more comments inside function than my newer code does though. Overall, the source I attached to my first post is representative of my commenting and style.

  • 0

Personally I think I sit somewhere in the middle. I believe that your comments should (for the mostpart) explain the "Why" in the code (i.e. why does this code do what it does) and let the code itself say WHAT it does. At the same time though, I think that people who insist on not commenting at all (the "all code should be self-documenting") are dangerous and make life hard for others.

I have another example that may fit what you are talking about, albeit a bit on the extreme side. I created the rather large comment block in this function because I kept confusing myself as to what exactly I need to cleanup in this function and what was taken care of by the thread it spawns. The system as a whole that this code is part of is rather complex. This may be the single largest comment block I have ever written inside of a function.


/*
Load the specified plugin.

Remarks:
WARNING: This function has high overhead!

Arguments:
File [in] Name and path of the plugin to load.

Return Value:
If the plugin was loaded the identification number of the thread will be returned.
If the plugin path is invalid or the plugin has already been loaded 0 will be returned.
*/
unsigned long PluginManager::LoadPlugin( const char * File )
{
if( File == NULL ) return 0;
if( this->IsPluginLoaded( File ) == true ) return 0;
this->Plugins.push_back( PluginThread() );
this->Plugins.back().File = File;
this->Plugins.back().Provider = new EVPlugin( this->ProviderBase, 0, this->ED, this->Tabs );
this->Plugins.back().Provider->InfoMsg() << "Loading plugin " << File << std::endl;
// Before we actually create the thread, here is a short analysis of how the members of this->Plugins.back() are used.
//
// File File name and path of the plugin to load.
// This is also used by us as a sentinel to tell when the thread is finished initializing.
// Since the file name is no longer needed after the plugin is loaded (and it is not guaranteed
// to be initialized after this function exits), NULL is used as the sentinel value.
//
// Provider The plugin service provider handle that allows the plugin to interface with Alekto.
// It must be initialized in this function because only PluginManager can access the base.
// However, it does not need to be deleted anywhere in this class.
// Since it is used by the plugin thread exclusively, it will be deleted before the thread exits.
//
// Plugin The loaded plugin's handle.
// It is both created by and destroyed by the thread.
//
// Handle The handle of the plugin's thread.
// This handle must be assigned in this function when the thread is created.
// However, since it is no longer valid after the thread exits, the thread is responsible for nullifying it.
// This nullification is not done until the thread is ready to exit.
// Therefore, it may be safely used as a sentinel for thread exit.
this->Plugins.back().Handle = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE) &PluginThreadProc, reinterpret_cast< void * >( &this->Plugins.back() ), 0, NULL );
while( this->Plugins.back().File != NULL ) Sleep( 1000 ); // Wait until the thread is done loading the plugin.
Sleep( 500 ); // Wait for another half-second, just to be sure the thread has had sufficient time to cleanup.
if( this->Plugins.back().Handle == NULL )
{
this->Plugins.pop_back();
return 0;
};
return EVPlugin::EncodeID( this->Plugins.back().Handle );
}
[/CODE]

  • 0

I have another example that may fit what you are talking about, albeit a bit on the extreme side. I created the rather large comment block in this function because I kept confusing myself as to what exactly I need to cleanup in this function and what was taken care of by the thread it spawns. The system as a whole that this code is part of is rather complex. This may be the single largest comment block I have ever written inside of a function.

FWIW, when I said "your" in that quote I was speaking generalities, it wasn't intended as a commentary on your commenting style :p

  • 0

People that say they don't comment because it's self evident are usually the ones who don't know what they're really doing and cause a massive headache for others once they are fired/leave and we're left with a jumbled mess of spaghetti code.

  • Like 2
  • 0

Well in .net I use the built-in XML style commenting system - a quick example:


/// <summary>
/// An example method to show 'Commenting Style'.
/// Performs no real functionality.
/// </summary>
/// <param name="FirstName">String - Input Persons Forename</param>
/// <param name="LastName">String - Input Persons Surname</param>
/// <param name="ID">Short Integer - Input Persons ID Number</param>
public void Example(string FirstName, string LastName, Int16 ID)
{
// Blah Blah Code Here
}
[/CODE]

  • 0

I comment more than I probably should, my code is clean and concise, but I like to outline my code in pseudo code before I write it, then I write the code to match the comments. So you could either read the comments to know what's happening, or read the code.

  • 0

I don't put comments in my code unless it's code I borrowed from some open source app, or something I got from a StackOverflow question. Then I will put what app it came from or the link to the SO question.

I also don't code for a living, nor do I do anything that is overly complex enough to actually require comments for someone to figure out what it does. I try to use function and variable names that would make it obvious to a reader what a particular block of code does. Same for class names.

  • 0

IMO I think you put in too many comments in. Code in my option should be simple and mostly self documenting. Keep It Simple and clean. Too much documentation can make it difficult to read actual code.

as some one said

I try to use function and variable names that would make it obvious to a reader what a particular block of code does. Same for class names.

return true; // Assume that everything ran successfully.

do you need to add that comment in ? ESPECIALLY when you have documented what it returns in the method header....

if you used a variable like 'hasRunSuccessfully' and then have 'return hasRunSuccessfully' it is essentially self documenting - no need for comments.

//

// Check for trouble.

//

check for 'trouble' not the best label - but programmers dont have to use the best labels, no need for the extra // just put in a blank line.

this->FoundFiles.clear(); // Clear our found files vector.

I would label this as FoundFileList or FoundFilesVector or FoundFileCollection and then if you have FoundFilesVector.Clear() it is pretty self evident what you are doing.

  • 0

IMO it boils down to adding relevant information where it's likely to help. There's no universal rule to determine what information is relevant and when it's likely to help. Rules like comment every function, or don't comment x or y, don't work. I am particularly vexed by any mandatory ASCII artwork like drawing boxes and lining up stars in front of every function. Complete waste of my time.

I think a combination of a clean, self-documenting coding style and unit testing goes a long way towards providing good understanding and confidence about a code base; unlike comments they can't lie and they can't go out of date unnoticed.

  • 0

I comment only where something is not clear and where the IDE makes use of them.

Our team follows the principals of self-explaniatory code. It's must easier to read a small function that does one thing with a set of self explanatory actions than a large function loaded with comments.

We'd probably write something closer to this for the example provided above:


unsigned long PluginManager::LoadPlugin( const char * fileName ) {

  if (!this-&gt;ShouldLoadPlugin( fileName ))
    return 0;

  PluginThread* pluginThread = this-&gt;CreatePluginThread( fileName );

  this-&gt;LoadPluginAndWait( pluginThread );

  return this-&gt;GetPluginResult( pluginThread );

}

  • Like 1
  • 0

By the way, the comments are the least of your worries about the last function you posted. In order of importance:

1)


while( this->Plugins.back().File != NULL ) Sleep( 1000 ); // Wait until the thread is done loading the plugin.
Sleep( 500 ); // Wait for another half-second, just to be sure the thread has had sufficient time to cleanup.[/CODE]

Waiting half a second doesn't guarantee any particular thread will run at all. This might come up 1 time out of 10000 in particular conditions which you might not even be able to reproduce. You should be using a proper thread synchronization mechanism such as a monitor here.

2)

[CODE] this->Plugins.back().Provider = new EVPlugin( this->ProviderBase, 0, this->ED, this->Tabs );[/CODE]

Any "new" operator in methods is a red flag. Exceptions can be thrown anywhere (including by the "new" operator itself) and prevent any cleanup code from executing, leading to memory leaks. Plus since there's no corresponding "delete" within the method, the cleanup responsibility is apparently held by some different code, which makes your memory management difficult to reason about (and probably wrong). The only way to guarantee proper cleanup in C++ is RAII, use it.

3) Some stylistic issues:

- Inconsistent use of C/C++-style casts:

[CODE]CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE) &PluginThreadProc, reinterpret_cast< void * >( &this->Plugins.back() ), 0, NULL );[/CODE]

- Using "this->" in front of every member access is just syntactic noise.

- If you're using a recent C++ compiler (VS2010 and above), use nullptr instead of NULL. nullptr is a pointer type, NULL is an integral constant. Consider:

[CODE]
void foo(int*);
void foo (int);

void bar() {
foo (NULL); // Calls 'foo(int)'
}[/CODE]

  • 0

By the way, the comments are the least of your worries about the last function you posted. In order of importance:

1)


while( this->Plugins.back().File != NULL ) Sleep( 1000 ); // Wait until the thread is done loading the plugin.
Sleep( 500 ); // Wait for another half-second, just to be sure the thread has had sufficient time to cleanup.[/CODE]

Waiting half a second doesn't guarantee any particular thread will run at all. This might come up 1 time out of 10000 in particular conditions which you might not even be able to reproduce. You should be using a proper thread synchronization mechanism such as a monitor here.

2)

[CODE] this->Plugins.back().Provider = new EVPlugin( this->ProviderBase, 0, this->ED, this->Tabs );[/CODE]

Any "new" operator in methods is a red flag. Exceptions can be thrown anywhere (including by the "new" operator itself) and prevent any cleanup code from executing, leading to memory leaks. Plus since there's no corresponding "delete" within the method, the cleanup responsibility is apparently held by some different code, which makes your memory management difficult to reason about (and probably wrong). The only way to guarantee proper cleanup in C++ is RAII, use it.

3) Some stylistic issues:

- Inconsistent use of C/C++-style casts:

[CODE]CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE) &PluginThreadProc, reinterpret_cast< void * >( &this->Plugins.back() ), 0, NULL );[/CODE]

- Using "this->" in front of every member access is just syntactic noise.

- If you're using a recent C++ compiler (VS2010 and above), use nullptr instead of NULL. nullptr is a pointer type, NULL is an integral constant. Consider:

[CODE]
void foo(int*);
void foo (int);

void bar() {
foo (NULL); // Calls 'foo(int)'
}[/CODE]

I really appreciate your feedback. Especially RAII, which I didn't know about before.

I do have one question though. I am using GCC 4.6.2 to compile the project the code excerpt you quoted in point 3 came from, so it does have (mostly complete) C++11 support and I can use nullptr. With the size of this project, however, it would take considerable effort to convert all of my null pointers to nullptr instead of NULL. What is the advantage of doing so?

  • 0

I do have one question though. I am using GCC 4.6.2 to compile the project the code excerpt you quoted in point 3 came from, so it does have (mostly complete) C++11 support and I can use nullptr. With the size of this project, however, it would take considerable effort to convert all of my null pointers to nullptr instead of NULL. What is the advantage of doing so?

You could just run find-and-replace over the entire project, if you're feeling bold. It should just work, in theory. The benefit of nullptr over NULL is in certain cases where there is ambiguity between integer and pointer type, where using NULL can result in unexpected bugs. nullptr is a proper pointer type unlike NULL, so it behaves as expected.
  • 0

I try to comment whenever possible in my code, block comments above classes / methods and small lines for clarification where it may be needed.

I find this method makes it easier when you get stuck, and helps identify logic errors more easily. Not only does it help to maintain the code but it helps me think through a de-bugging process.

If I know somebody else will be reading / maintaining / using my code I make sure I have legible nearly organised comments.

This topic is now closed to further replies.
  • Posts

    • NetSpeedTray 1.3.3 by Razvan Serea NetSpeedTray is a lightweight, open-source Windows network monitor that shows live upload and download speeds directly on the Taskbar. Designed for efficiency, it quietly sits in the system tray, conserving CPU and battery with dynamic updates. It blends seamlessly with Windows 10/11, adapts to light/dark themes, and auto-positions to avoid overlaps. Features include accurate interface detection, customizable display, optional mini-graph, color coding, granular font and unit control, detailed per-interface history graphs, safe data management, and easy CSV export—bringing the network monitoring Windows forgot. NetSpeedTray key features: Lightweight & Efficient Runs quietly in your system tray without consuming resources. Features a "Dynamic Update Rate" that lowers refresh frequency when the network is idle to save CPU and battery life. Native Look & Feel Blends seamlessly with Windows 10/11 UI. Smart detection for light and dark taskbar themes ensures text is always visible. Intelligent & Adaptive Positioning Automatically finds empty space next to your system tray and shifts to make room for new icons, preventing overlaps. Seamless OS Integration Behaves like a native Windows component. Hides instantly with auto-hiding taskbar Hides when a fullscreen app is active Smart Network Monitoring Accurate by Default: Auto mode identifies your main internet connection and ignores noise from VPNs or virtual adapters. Easy Interface Selection: Switch effortlessly between Auto, All, or Selected network interfaces via intuitive radio buttons. Total Visual Customization Free Move Mode: Unlock and place the widget anywhere on your screen. Optional Mini-Graph: Real-time graph of recent network activity with adjustable opacity. Color Coding: Customize colors and speed thresholds to quickly see network status. Granular Display Control Text & Font: Adjust font family, size, weight, and alignment. Units: Automatic (B/s, KB/s, MB/s) or fixed Mbps display. Precision: Set decimal places and always show them for uniform appearance. Detailed & Intelligent History Graph Smart Scale: Logarithmic scale shows low-level traffic and large spikes clearly. Per-Interface Filtering: View speed history for specific adapters (Wi-Fi, Ethernet, VPN). Safe & Efficient Data Management: Adjustable retention, automatic cleanup, optimized database. Easy Data Export: Export raw data to .csv or save high-quality graphs for reports. NetSpeedTray v1.3.3: The Updater Fix A stabilization release that repairs a critical regression in v1.3.2: the app shipped without OpenSSL, which silently broke every HTTPS request — including the built-in update checker (the "Could not check for updates" error many of you hit). This release restores it, hardens the build so it can't happen again, and fixes a startup crash plus four other reported bugs. Changes: Fixed update checking — Resolved a critical issue that prevented the app from checking for updates ("Could not check for updates"). Fixed startup crash with Auto-Cycling — The app no longer crashes on launch after enabling Cycle display mode. Fixed incorrect network speeds on 10GbE adapters — Multi-gigabit network cards now display speeds correctly instead of being stuck at 0. Improved color coding — Default color is shown when idle, and color/threshold changes now apply immediately without restarting. Fullscreen visibility fix — The widget now correctly stays visible over fullscreen apps when Keep Visible is enabled. Improved AMD Ryzen temperature detection — More reliable CPU temperature monitoring for Ryzen processors. Cleaner upgrades — Installer now removes outdated application files during upgrades, preventing DLL/version conflicts while preserving user settings. Improved stability — Fixed potential DLL loading issues by excluding critical OpenSSL and NumPy components from UPX compression. Better settings window — Scrollbars removed and layout improved for a cleaner experience. Localization improvements — Updated translations and completed missing UI text across all supported languages. More reliable releases — Added regression tests covering recent critical fixes, bringing the test suite to 196 passing tests. [full release notes] Download: NetSpeedTray 1.3.3 | 87.9 MB (Open Source) Download: NetSpeedTray Portable | 101.0 MB View: NetSpeedTray Home Page | Screenshot Get alerted to all of our Software updates on Twitter at @NeowinSoftware
    • Why Delta Chat is the best decentralized messenger you have probably never tried by Paul Hill There is no shortage of messaging apps out there; we have WhatsApp, Messenger, and Telegram, just to name a few. While Meta has taken steps to incorporate encryption into Messenger and WhatsApp, they still leave a lot to be desired. If you are in the market for a messaging app that promotes security, privacy, and optional anonymity, you'll want to read what I have to say about Delta Chat. For those not familiar with Delta Chat, rather than relying on centralized servers as you do with Facebook Messenger, it relies on email. Essentially, it is a chat interface that feels like a messaging app, but secretly in the background, it is firing off emails. In the past, you used to have to sign in with your email account. When you sent messages to people, it would just be sending encrypted messages to their inbox, which their Delta Chat client would decrypt. When I first learned about Delta Chat, it required users to sign in with an email account, but I was pleasantly surprised upon trying it in 2026 that this is no longer a requirement, or the preferred method was to use the app. Recently, I’ve tried UAD-ng on my old Nokia 3.4 to disable most of the Google apps because the bootloader is locked, and this is the next best option. While finding replacement apps in F-Droid, I came across Delta Chat again, and it has undergone quite a big change since I last used it, with its new chatmail relays, which no longer require you to sign in to your own email account, providing anonymity, and they offer greater security. Android and Desktop Delta Chat apps. Not only does it run on my de-googled phone, but it also works on desktop computers and iOS, making it truly ubiquitous. For me, Delta Chat is a wonderful alternative messenger because it gives you more control. It supports switching between different profiles, which you can set up super quickly; you don’t register a username, you don’t register a password. The only thing you do have is a random string email address on a chatmail relay (which you don’t have to memorize). To maintain access to your profile, you just need to add a second device to your account via QR code or make a backup of your account, which you can restore later. Fail to do these, your account is gone - as it should be if you don’t want to leave accounts that could get hacked later on. My decision to block Google stuff on my Nokia was done for practical reasons; the device sucked when it launched, and it sucks even more now. The nice thing about F-Droid and the apps within is that they’re usually lightweight, free of bloat, and work well on that device. What was inconvenient for me was that it was hard to send messages from that device, say if I wanted to copy a code over to my main phone or send family members a link from that device. That’s when I decided to look at the available chat apps and saw Delta Chat. Another nice thing about Delta Chat is its notifications. Some messaging apps rely on Google’s ecosystem for notification transport on Android; however, with Delta Chat, it can use Google’s solutions if you have Play Services or MicroG installed. Otherwise, it is able to keep a background connection to the chatmail relay server so that you can get notified when you receive a message. As free software, the code of Delta Chat is open for all who want to take it and build upon it. In the future, if the developers of Delta Chat make a catastrophically bad decision and take the app in an undesirable direction, users can take the code and fork the project. This contrasts with closed-source apps from corporations that can take their products in any direction they like. By relying on free software instead of closed-source programs, you actually control your computing. I’ve spoken at length about how running this type of software is like owning your own home rather than renting it. The same applies here; if you use Delta Chat, you don’t need to worry about it going away in the future. Whether it is Telegram, WhatsApp, or Messenger, you are required to register a username and password to use these services. A major flaw in this design is that anyone can try various passwords and potentially break into your account with your complete chat history intact. Sure, there is encryption in Messenger, where you need a second PIN and two-factor authentication in Telegram, but breaches happen all the time. Unlike before, when you used to sign in to your email account to send and receive messages, the primary way to do it now is to create an account on a chatmail relay. The resulting email address is a random string followed by the name of the relay you pick. This means you can start and begin adding contacts Without a username and password, you either need to ensure you have a backup or at least one device running your Delta Chat profile. The primary way to log in on another device is to go to the settings and add a second device. Then, you’ll just scan a QR code with your new device, and it’ll log in to your account and sync all your chat history and contacts. To end users, Delta Chat just looks like any instant messenger; however, it is really sending your messages as encrypted emails to your contact. This is pretty cool from a censorship perspective, as it makes the service more difficult to block. Previously, the main way to use the app was by logging in with email, but nowadays, it’s recommended that you use chatmail relays. Chatmail relays temporarily hold messages in case your device is offline. They are cheap, simple servers that don’t store data as group states. Other information, like your name and avatar, only exists on your device and the devices of those you share your contact information with. The relays are also decentralized and operated by various groups and individuals. It is even possible to set up your own chatmail relay, but most people will want to use one hosted elsewhere. To keep your messages secure, Delta Chat uses a secure subset of the OpenPGP standard that gives you automatic end-to-end encryption. It also uses Secure-Join to exchange encryption setup information through QR-code scanning or invite links. Autocrypt is also used to automatically establish end-to-end encryption between contacts and all members of group chat, but sometime this year Autocrypt v2 will be rolled out, bringing post-quantum resistant encryption and forward secrecy. The Delta Chat FAQ is an interesting read that explains many more details about the app. Credit: Pexels Delta Chat is unique among messaging apps because it is built on email, a technology that’s decades old and isn’t going anywhere soon. What’s more is that email is not centralized either, so it’s far more difficult for any authoritarian regime to disrupt the Delta Chat app. I haven’t spoken too much about features yet, so I will do that now. Delta Chat allows you to do one-on-one chats, group chats, and create channels. It also supports file sharing and making audio and video calls when chatting one-to-one, but it’s not available for group chats right now. At the time of writing, the calling functionality is disabled and can be enabled in Settings > Advanced > Debug Calls. I have used the video calling feature, and the quality is excellent. It works over WebRTC, another open standard. The app also lets you send voice notes, enables disappearing messages, and has its own app ecosystem. I did try playing chess one time there, but it was a bit spotty; though, we did manage to complete the game with a victory for me. To add people to Delta Chat, you can either give them your Delta Chat link or your QR code to scan. These are the only ways to add users, so you won't have any spam bots bothering you. If the people you want to chat with don't have the app yet, just send them your link, and it will take them to a webpage where they can install the app and then add you. It's really quick for them to install it and get started, which is nice. Credit: Microsoft. The Majorana 2 quantum chip unveiled in 2026. I do not think quantum computers are too far out now, and I do hope that Delta Chat is able to push out Autocrypt v2 sooner, rather than later, so bad actors do not attempt to collect encrypted communications and then decrypt them in the future using quantum computers. By getting people’s messages post-quantum-safe now, users won’t have to worry when quantum computers start cracking legacy encryption. Overall, I would recommend this app to people who are already past WhatsApp and Messenger and have perhaps begun using apps like Telegram or Session. It shares a lot of characteristics with these apps and goes a lot further than Telegram in terms of security. By being based on email, it is also resistant to censorship, and the lack of a username and password makes you anonymous (if you want to be) and safe from brute force password cracking attempts. Let me know in the comments if you’ve tried Delta Chat recently. Do you think it's a good bulwark against governments that are tightening their grip on the internet?
    • Putin was behind Farage/Brexit and behind Trump/MAGA. Different idiot lying beasts, same fascist master. Same screwed up results for both nations.
  • Recent Achievements

    • One Year In
      bernmeister earned a badge
      One Year In
    • Week One Done
      Scoobystu earned a badge
      Week One Done
    • Week One Done
      tuben earned a badge
      Week One Done
    • First Post
      OffsetAbs earned a badge
      First Post
    • Reacting Well
      OffsetAbs earned a badge
      Reacting Well
  • Popular Contributors

    1. 1
      +primortal
      464
    2. 2
      +Edouard
      217
    3. 3
      PsYcHoKiLLa
      154
    4. 4
      Steven P.
      73
    5. 5
      FloatingFatMan
      71
  • Tell a friend

    Love Neowin? Tell a friend!