Thursday, July 2, 2026

Javascript module

JavaScript Modules (ES6) let you split code into multiple files and reuse code cleanly.

Main keywords:

  • export → make something available from a file to outside.

  • import → use something from another file into a file.

export:

Use export when you want to share variables, functions, or classes from a file.

math.js

export const pi = 3.14;

export function add(a, b) {
return a + b;
}

Here, pi and add() can be used in other files.

import:

Use import to bring exported things into another file.

app.js

import { pi, add } from "./math.js";

console.log(pi); // 3.14
console.log(add(2, 3)); // 5

Default Export

A file can have one default export.

user.js

export default function greet() {
console.log("Hello");
}

Import it without {}

app.js

import greet from "./user.js";

greet();

Thursday, November 6, 2025

Element of a good table (Ref: Database design mere mortals by Michael J. Hernandez)

 Elements of the Ideal Table:

  1. It represents a single subject, which can be an object or event that reduces the risk of potential data integrity problems.

  2. It has a primary key. This is important for two reasons: A primary key uniquely identifies each record within a table, and it plays a key role (no pun intended) in establishing table relationships.

  3. It does not contain multipart or multi-valued fields.

  4. It does not contain calculated fields.

Ex:

Table orders:


id

unit_price

quantity

total_amount

1

10

5

50

2

5

2

10

This is bad table design.

 Problems:

  1. Redundancytotal_amount = unit_price * quantity is stored unnecessarily.

  2. Inconsistency risk → If quantity is updated but total_amount isn’t recalculated, the data becomes wrong.

  3. Extra storage → You’re saving the same information twice (derived + original).

Solution:

id

unit_price

quantity

1

10

5

2

5

2

5. It does not contain unnecessary duplicate fields.

6.It contains only an absolute minimum amount of redundant data.


Wednesday, September 24, 2025

Clean code chapter 3(Robert C.martin)

 

Summary: -------- 

1. Functions should hardly ever be 20 lines long.

 2.Keep blocks (inside if, else, while, for, etc.) short.

 Ideally, just one line long. And that line should usually be a function call with a descriptive name. Don’t let functions get so big that they require deep nesting.

 If you see more than 1–2 levels of indentation, it’s a smell.

 Break the nested logic into smaller functions.

 Example: Problem: Deep nesting(messy)

 public void handleOrder(Order order) {
    if (order != null) {
        if (order.hasItems()) {
            for (Item item : order.getItems()) {
                if (item.isInStock()) {
                    // process item
                    System.out.println("Processing item: " +                                 item.getName());
                    item.setStatus("PROCESSED");
                } else {
                    System.out.println("Item out of stock: " +                             item.getName());
                }
            }
        } else {
            System.out.println("Order has no items.");
        }
    } else {
        System.out.println("Order is null.");
    }
}

Clean Code:

 public void handleOrder(Order order) {
    if (isProcessable(order)) {
        processOrder(order);
    }
}


// ---- helper functions ----

private boolean isProcessable(Order order) {
    return order != null && order.hasItems();
}

private void processOrder(Order order) {
    for (Item item : order.getItems()) {
        processItem(item);
    }
}

private void processItem(Item item) {
    if (item.isInStock()) {
        markItemAsProcessed(item);
    } else {
        logOutOfStock(item);
    }
}

private void markItemAsProcessed(Item item) {
    System.out.println("Processing item: " + item.getName());
    item.setStatus("PROCESSED");
}

private void logOutOfStock(Item item) {
    System.out.println("Item out of stock: " + item.getName());
}

3. Do one thing:   

If you can extract a piece of code into a well-named function that adds understanding, your original function is probably doing more than one thing.

Friday, August 1, 2025

Testing controller

------Controller-------------

@RestController
@RequestMapping("/items")
public class ItemController {
    private final ItemService itemService;    

@Autowired
    public ItemController(ItemService itemService) {
        this.itemService = itemService;
    }
    @GetMapping
    public ResponseEntity<List<Item>> getAllItems() {
        List<Item> allItems = itemService.findAllItems();
        return ResponseEntity.ok(allItems);
    }
}

----------Service---------

@Slf4j
@Service
public class ItemServiceImpl implements ItemService {
    @Override
    public List<Item> findAllItems() {
        return Arrays.asList(new Item(BigInteger.valueOf(1), "pen", 10), new Item(BigInteger.valueOf(2), "pencil", 5));
    }
}
 

Now we want to test ItemController. we can do this by the below way ----

-------Test code---------------------

@WebMvcTest(ItemController.class) // load only mvc part not full app.
public class ItemControllerTest {
    @Autowired
    private MockMvc mockMvc; // can simulate http request without start server
    @MockBean
    private ItemService itemService;
    @Test
    void testFindAllItems() {
        // arrange
        List<Item> items = Arrays.asList(new Item(BigInteger.valueOf(1), "pen", 10),
                new Item(BigInteger.valueOf(2), "pencil", 5));
        when(itemService.findAllItems()).thenReturn(items);//stub so no actual method .
        try {
            // act
            ResultActions resultActions = mockMvc.perform(get("/items").contentType(MediaType.APPLICATION_JSON));
            // assert
            resultActions.andExpect(status().isOk())
            .andExpect(jsonPath("$.size()").value(2))
                    .andExpect(jsonPath("$[0].title").value("pen"))
                    .andExpect(jsonPath("$[1].price").value(5));

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

Notes:
--------

 1.@WebMvcTest(ItemController.class) : Load only MVC part not full app.

2. MockMvc :  Can simulate HTTP request without start server.

3. . $ : Root of the response json.

 

Wednesday, July 30, 2025

Dummy Vs Stub Vs fake

 Do I need to pass an object just to satisfy the method signature?
→ Use a Dummy

Do I need to simulate return values (e.g., from a repository)?
→ Use a Stub

Do I need to simulate real logic or store data?
→ Use a Fake

Wednesday, March 12, 2025

Abstract factory pattern

When single task can be done by multiple groups/family of objects and decision is taken at the runtime.




 

Tuesday, November 5, 2024

Fluent interface pattern

 public class UserConfigurationManager {
    private String userName;
    private String password;
    private UserConfigurationManager() {
    }
    public UserConfigurationManager setUserName(String userName) {
        this.userName = userName;
        return this;
    }
    public UserConfigurationManager setPassword(String password) {
        this.password = password;
        return this;
    }
    public static UserConfigurationManager make(Consumer<UserConfigurationManager> consumer) {
        System.out.println("making configuration..........");
        UserConfigurationManager configurationManager = new UserConfigurationManager();
        consumer.accept(configurationManager);
        return configurationManager;
    }
}

public class MainApp {
    public static void main(String[] args) {
        UserConfigurationManager.make(configManager -> configManager.setUserName("lokman").setPassword("12345"));
    }

Note: Fluent pattern can be used to define Model in JPA

Monday, October 21, 2024

Command Design Pattern using lambda expression

 Command design pattern: The main concept of command pattern is how the command will be executed that process is encapsulated in a class . So the the process will be hide into that class.

Command :  Command object knows about receiver and call a method of receiver.

Receiver:  Who receives command and acts based on that command;

Invoker: Who only have reference of command interface . Who does not know about the implementation of the command . 

Client: It hold all the list of command. Just invoke to the invoker whatever need .

Example:

Command:

public interface Command {
    void execute();
}

public class OnCommand implements Command {
    private Tv tv;
    public OnCommand(Tv tv) {
        this.tv = tv;
    }
    @Override
    public void execute() {
        tv.switchOn();
    }
}

 public class OffCommand implements Command {
    private Tv tv;
    public OffCommand(Tv tv) {
        this.tv = tv;
    }
    @Override
    public void execute() {
        tv.switchOff();
    }
}

Invoker:

public class RemoteControl {
    private List<Command> history = new ArrayList<>();
    public void press(Command command) {
        history.add(command);
        command.execute();
    }
}

Client:

public class TvClient {
    public static void main(String[] args) {
        Tv tv = new Tv();

        Command onCommand = new OnCommand(tv);

        Command offCommand = new OffCommand(tv);
        RemoteControl remote = new RemoteControl();
        remote.press(onCommand);
        remote.press(offCommand);
    }
}

Here command interface has  one abstract method. So we can use lambda expression here . so no need to create OnCommand and OffCommand class .

public class TvClient {
    public static void main(String[] args) {
        Tv tv = new Tv();
        RemoteControl remote = new RemoteControl();
        remote.press(tv - > switchOn);
        remote.press(tv -> switchOff);

    }

we can replace it using method reference also ,

public class TvClient {
    public static void main(String[] args) {
        Tv tv = new Tv();
        RemoteControl remote = new RemoteControl();
        remote.press(tv:: switchOn);
        remote.press(tv:: switchOff);

    }
}

Sunday, October 6, 2024

What is lamda expression and how it solve real world problem?

 A lambda expression is the representation of anonymous function(function that doesn't have any function name)lambda expression is key feature of functional programming. Functional programming is followed by declarative programming approach . 

Declarative programming: Declarative programming is a programming approach where main focus is on what is need but not how to do .

Now lets have an example where we want to filter Contact information based on certain criteria .

Model:

@Getter

@Setter

@ToString

public class Contact {
    public enum Gender {
        MALE, FEMALE
    };
    private String name;
    private String email;
    private int age;
    private Gender gender;
    public Contact(String name, String email, int age, Gender gender) {
        this.name = name;
        this.email = email;
        this.age = age;
        this.gender = gender;
    }
}

Now we want to filter those contact whose age is greater than 18 and less than 25 and gender MALE. 

So what can we do ?

Ans: We can create a ContactFilterService class that will filter and return filtered contacts.

public class ContactFilterService {
public List<Contact> findContactsAgeBetween18to25AndMale(List<Contact> contacts) {
List<Contact> filteredContacts = new ArrayList<>();
for (Contact contact : contacts) {
      if (18 <= contact.getAge() && contact.getAge() <= 25
       && contact.getGender() == Gender.MALE) {
        filteredContacts.add(contact);
        }
    }
    return filteredContacts;
    }

Now I want to filter contacts where age is age is greater than 18 and less than 25 and gender FEMALE.

So we can can create another method to do that . 

But ContactFilterService growing , because in every need we are creating new methods and we are duplicating code.

We can create a generic method in ContactFilterService class that only check the condition .

We don't want to modify this class every time . We want to pass the filter logic from outside of the class .

lets do that,

Interface:

 public interface FilterCriteria {
    boolean match(Contact contact);

}

ContactFilterService:

public class ContactFilterService {

    public List<Contact> filter(List<Contact> contacts, FilterCriteria criteria) {
        List<Contact> filteredContacts = new ArrayList<>();
        for (Contact contact : contacts) {
            if (criteria.match(contact)) {
                filteredContacts.add(contact);
            }
        }
        return filteredContacts;
    }

} 

MainApp:

 public class MainApp {
    public static void main(String[] args) {

     Contact contact1 = new Contact("rupta", "rupta@gmail.com", 25, Contact.Gender.FEMALE);
        Contact contact2 = new Contact("asik", "asik@gmail.com", 30, Contact.Gender.MALE);
        Contact contact3 = new Contact("ruhul", "ruhul@gmail.com", 22, Contact.Gender.MALE);
        Contact contact4 = new Contact("nusrat", "nusrat@gmail.com", 31,                                           Contact.Gender.FEMALE);
        List<Contact> contacts = Arrays.asList(contact1, contact2, contact3, contact4);
        ContactFilterService contactFilterService = new ContactFilterService();

         List<Contact> filterdList = contactFilterService.filter(contacts, new FilterCriteria() {
            @Override
            public boolean match(Contact contact) {    
                if(18 <= contact.getAge() && contact.getAge() <= 25) {
                    return true;
                }
                return false;
            }
        });
        System.out.println(filterdList);
    }

    }

}

 Here we are just passing our logic into ContactFilterService filter() method.

So we no need to create new method every time .

 Now we have solved one problem but we still implementing anonymous inner class as boilerplate code.

every time we are writing new FilterCriteria() and @Override and return type of the function.

So here we can pass lambda expression. 

lets refactor our code,

 List<Contact> filterdList = contactFilterService.filter(contacts, (Contact contact) -> {
            return 18 <= contact.getAge() && contact.getAge() <= 25;
        });
        System.out.println(filterdList);

Here lamda expression,

(Contact contact) -> {
            return 18 <= contact.getAge() && contact.getAge() <= 25;
        }

But think in mind ,

Lambda expression will apply in Functional Interface only . 

A functional interface only have one abstract method.

The type of lambda expression is functional interface(interface that has at least an abstract method).

The target type of the lambda expression is functional interface .



 


Friday, August 30, 2024

High level overview of rabbitMQ

Messaging in software industry is the process of sending message between applications or services in a loose couple manner . The application can be written in different languages or can be in different platform does not matter .

RabbitMQ implements AMQP(Advanced Message Queuing protocol). 

One of the major benefit of using RabbitMQ is that , a TCP connection can accomodate multiple channel . So no need to open multiple TCP connection and close them to RabbitMQ broker.


Tuesday, July 9, 2024

Implement queue using Double ended Linked List

 To implement a queue using Linkedlist we can use double ended Linkedlist.

A double ended Linkedlist has first and last node pointer. So we can insert item at last to implement enqueue operation  using last pointer. To implement dequeue operation we can remove item from the first using first node.

Below is the implementation:

class Link:

public class Link {
    public long data;
    public Link next;
    public Link(long data) {
        this.data = data;
    }
    public void displayLink() {
        System.out.println("data :"+ data);
    }
}

class FirstLastList:

public class FirstLastList {
    private Link first;
    private Link last;
    public FirstLastList() {
        first = null;
        last = null;
    }
    public boolean isEmpty() {
        return first == null;
    }
    public void insertLast(long data) {
        Link newLink = new Link(data);
        if (isEmpty()) {
            first = newLink;
        } else {
            last.next = newLink;
        }
        last = newLink;
    }
    public long removeFirst() {  
        Link temp = first;
        first = first.next;
        return temp.data;
    }
    public void display() {
        Link current = first;
        while (current != null) {
            System.out.print(current.data + "-->");
            current = current.next;
        }
    }
}

class LinkQueue:

public class LinkQueue {
    private FirstLastList firstLastList;
    public LinkQueue() {
        this.firstLastList = new FirstLastList();
    }
    public void enqueue(long data) {
        firstLastList.insertLast(data);
    }
    public void dequeue() {
        firstLastList.removeFirst();
    }
    public void displayQueue() {  
        firstLastList.display();
    }
}

class MainApp:
public class MainApp {
    public static void main(String[] args) {
        LinkQueue queue = new LinkQueue();
        queue.displayQueue();
        System.out.println("after enqueue new item");
        queue.enqueue(10);
        queue.enqueue(20);
        queue.enqueue(30);
        queue.enqueue(40);
        queue.enqueue(50);
       
        queue.displayQueue();
        System.out.println("\n");
       
        queue.dequeue();
        System.out.println("after dequeue item");
        queue.displayQueue();
    }
}

Monday, July 8, 2024

Implemented Stack using LinkedList

Link class: 

public class Link {
    public long data;
    public Link next;
    public Link(long data) {
        this.data = data;
    }
    public void displayLink() {
        System.out.println("data :"+ data);
    }
}

 LinkedList class:

public class LinkedList {
    private Link first;
    public LinkedList() {
        first = null;
    }
    public boolean isEmpty() {
        return first == null;
    }
    public void insertFirst(long data) {
        Link newLink = new Link(data);
        newLink.next = first;
        first = newLink;
    }
    public long deleteFirst() {
        Link temp = first;
        first = first.next;
        return temp.data;
    }
    public void displayList() {
        Link current = first;
        while (current != null) {
            System.out.println(current.data + "----");
            current = current.next;
        }
    }
}

 LinkStack class:
public class LinkStack {
    private LinkedList theList;
    public LinkStack() {
        theList = new LinkedList();
    }
    public void push(long data) {
        theList.insertFirst(data);
    }
    public void pop() {
        theList.deleteFirst();
    }
    public boolean isEmpty() {
        return theList.isEmpty();
    }
    public void displayList() {
        System.out.println("displaying the stack---------------");
        theList.displayList();
    }
}

MainApp:

 public class MainApp {
    public static void main(String[] args) {
        LinkStack stack = new LinkStack();
        stack.push(10);
        stack.push(20);
        stack.push(30);
        stack.push(40);
        stack.push(50);
        stack.displayList();
        stack.pop();
        stack.displayList();
    }
}

Friday, June 14, 2024

How authenticationProvide interact with UserDetailService?

 

The AuthenticationProvider is the component that implements the authentication logic and uses the UserDetailsService to load details about the user. To find the user by username, it calls the loadUserByUsername(String username) method.

Thursday, June 13, 2024

Relationship between UserDetailService, UserDetails and UserDetails manager

 

The UserDetailsService returns the details of a user, finding the user by its name. The UserDetails contract describes the user. A user has one or more authorities, represented by the GrantedAuthority interface. To add operations such as create, delete, or change password to the user, the UserDetailsManager contract extends UserDetailsService to add operations.

Tuesday, February 13, 2024

Command design pattern

 When to implement ?

Suppose we have a situation ,

There are multiple action to execute based on a single value or criteria. There are several solution to do . We can use if.. else if ladder or switch case to do that  and define the action in each separate method . But if the action feature grow then we need to refactor and transfer the defined action in class . It will we time consuming . So in that situation we can implement Command design pattern like below.

 interface CampaignContactCommand {
        void execute();
    }
    
    class PauseContactCommand implements CampaignContactCommand {

        private ContactHelperNode node;

        public PauseContactCommand(ContactHelperNode node) {
            this.node = node;
        }

        @Override
        public void execute() {
            ContactPauseHelper helper = new ContactPauseHelper(node);
            helper.pauseContact();
            helper.release();
        }
    }
    
    class ResumeContactCommand implements CampaignContactCommand{

        private ContactHelperNode node;
        
        public ResumeContactCommand(ContactHelperNode node) {
            this.node = node;
        }
        
        @Override
        public void execute() {
            ContactResumeHelper helper = new ContactResumeHelper(node);
            helper.resumeContact();
            helper.release();
        }
    }
    
    class UnsubscribeContactCommand implements CampaignContactCommand {

        private ContactHelperNode node;

        public UnsubscribeContactCommand(ContactHelperNode node) {
            this.node = node;
        }

        @Override
        public void execute() {
            ContactUnSubHelper helper = new ContactUnSubHelper(node);
            helper.unSubContact();
            helper.release();
        }
    }
    
    class CampaignContactCommandFactory {
        private ContactHelperNode node;

        public CampaignContactCommandFactory(ContactHelperNode node) {
            this.node = node;
        }

        public CampaignContactCommand createCommand() {

            switch (node.getRequest().getRequestType()) {

            case PAUSE:
                return new PauseContactCommand(node);
            case RESUME:
                return new ResumeContactCommand(node);
            case UNSUB:
                return new UnsubscribeContactCommand(node);
            default:
                return null;
            }
        }
    }

Monday, December 25, 2023

Upload file to S3 api

 @Path("files")
@Log4j2
@Component
public class FileUploadController implements ConfigInterface{

    @Context
    private SecurityContext securityContext;
    
    @Inject
    private FileUploadAPIValidator paramValidator;
    
    @Path("/config")
    public Resource getConfigResource() {
        return Resource.from(ConfigController.class);
    }
    
    /**
     * @author lokman 20/11/2022
     *
     */
    @POST
    @Secured
    @Path("/upload")
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    @Produces(MediaType.APPLICATION_JSON)
    @RequestTracingFilter
    public Response uploadMultipleFile(@FormDataParam("files") List<FormDataBodyPart> files, @FormDataParam("files") List<FormDataContentDisposition> fileDetails) {
        
        log.info("file size :"+ fileDetails.size());
        
        JSONObject response = new JSONObject();
        
        BasicCacheUserPrincipal userPrincipal = (BasicCacheUserPrincipal) securityContext.getUserPrincipal();

        if (userPrincipal == null || userPrincipal.getUser() == null) {
            response.put("responseCode", HttpStatus.SC_UNAUTHORIZED);
            response.put("message", "Unauthorized");
            response.put("success", Boolean.FALSE);

            return Response.status(HttpStatus.SC_UNAUTHORIZED).entity(response.toString()).build();
        }
        
        if (fileDetails.size() == 1 && StringUtil.isBlank(fileDetails.get(0).getFileName())) {

            response.put("responseCode", HttpStatus.SC_OK);
            response.put("message", "No file selected.");
            response.put("success", Boolean.FALSE);

            return Response.status(HttpStatus.SC_UNAUTHORIZED).entity(response.toString()).build();
        }
        
        if (fileDetails.size() > DefaultConfig.MAX_FILE_SIZE) {
            response.put("responseCode", HttpStatus.SC_OK);
            response.put("message", "Maximum 10 files allowed.");
            response.put("success", Boolean.FALSE);

            return Response.status(HttpStatus.SC_UNAUTHORIZED).entity(response.toString()).build();
        }
        
        List<String> fileUrls = new ArrayList<>();
        
        for (int j = 0; j < files.size(); j++) {
            
            FormDataBodyPart formDataBodyPartFile = files.get(j);
            ContentDisposition contentDispositionHeader = formDataBodyPartFile.getContentDisposition();
            InputStream fileInputStream = formDataBodyPartFile.getValueAs(InputStream.class);
            FormDataContentDisposition fileDetail = (FormDataContentDisposition) contentDispositionHeader;
            
            FileUploadAPIValidationResponse verifyResponse = paramValidator.verifyFiles(fileInputStream, fileDetail);
            
            File tempFile = null;
            if(verifyResponse.isValid()) {
                String fileName = userPrincipal.getUser().getId() + "_" + "form" + "_" + System.currentTimeMillis() + "_" + fileDetail.getFileName();
                
                log.info("fileName :"+ fileName);
                
                tempFile = new File(fileName);
                
                FileOutputStream fileOutputStream = writeToFile(verifyResponse.getInputStream(), tempFile);
                log.debug("file length : "+ tempFile.length());
                
                if ((tempFile.length() / CommonUtils.ONE_MEGABYTE_IN_BYTES) > DefaultConfig.MAX_FILE_SIZE) {
                    removeExistingFile(tempFile);
                    fileOutputStream = null;
                    continue;
                }
                
                if (fileOutputStream != null) {
                    String fileUrl = uploadFileToS3(tempFile);
                    if (StringUtil.isNotBlank(fileUrl)) {
                        log.info("fileUrl :" + fileUrl);
                        
                        fileUrls.add(fileUrl);
                    }
                }
            }
            if(tempFile != null) {
                removeExistingFile(tempFile);
            }
        }
        
        log.info("fileUrls :"+ fileUrls.size());
        
        if(fileUrls.isEmpty()) {
            response.put("responseCode", HttpStatus.SC_OK);
            response.put("message", "File upload failed.");
            response.put("success", Boolean.FALSE);
            return Response.status(HttpStatus.SC_OK).entity(response.toString()).build();
        }
        
        response.put("responseCode", HttpStatus.SC_OK);
        response.put("message", "File uploaded successfully.");
        response.put("success", Boolean.TRUE);
        response.put("urls", fileUrls);
        return Response.status(HttpStatus.SC_OK).entity(response.toString()).build();
    }
    
    /**
     * @author lokman 19/11/2022
     *
     */
    private FileOutputStream writeToFile(InputStream inputStream, File tempFile) {
        
        byte[] data = new byte[1024];

        FileOutputStream fileOutputStream = null;
        int read = 0;
        try {
            fileOutputStream = new FileOutputStream(tempFile);
            while ((read = inputStream.read(data)) != -1) {
                fileOutputStream.write(data, 0, read);
            }
            fileOutputStream.flush();
            fileOutputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return fileOutputStream;
    }
    
    /**
     * @author lokman 19/11/2022
     * @param file
     */
    private String uploadFileToS3(File file) {
        try {
            String folder = S3Config.AWS3_SECTION_USER+"/";
            log.debug("Folder : "+folder);
            AWS3ApiResponse response = AWS3APIFactory.getAWS3API().uploadPublicFile(S3Config.AWS3_BUCKET_NAME, folder, file);
            log.info(" s3 upload Success : "+response.isSuccess());
            if(response.isSuccess()) {
                log.info("FileUrl : "+response.getFileUrl());
                return response.getFileUrl();
            }
        } catch (Exception e) {
            log.error("Exception : "+ e);
        }
        return null;
    }
    
    /**
     * @author lokman 19/11/2022
     * @param file
     */
    private void removeExistingFile(File file) {
        try {
            if (file.exists()) {
                boolean fileRemoved = file.delete();
                log.debug("fileRemoved : " + fileRemoved);
            }
        } catch (Exception e) {
            log.error("errorMessage ", e);
        }
    }
}

Javascript module

JavaScript Modules (ES6) let you split code into multiple files and reuse code cleanly. Main keywords: export → make something available...