CS50 Vigenere program
It's usually best to return as early as possible for invalid cases. This avoids unnecessary nesting and complexity.

e.g. the main() function has a lot of redundant code:

int main(int argc, string argv[])
{
    //check for valid key and save
    int valid = 0;
    if (argc == 2)
    {
        valid = check(argv[1]);        
    }
    else 
    {
        printf("Usage: ./vigenere keyword\n");
        return 1;
    }

    ...
    if (valid == 1)
    {
        ...
    }
    else
    {
        printf("Usage: ./vigenere keyword\n");  
        return 1;
    }
}


and could be shortened to:

int main(int argc, string argv[])
{
    if (argc != 2 || check(argv[1]) != 0)
    {
        printf("Usage: ./vigenere keyword\n");
        return 1;
    }

    ...
}


The main loop of the program could be abstracted to a separate function (after checking for a valid key / printing usage on error).



Similarly check() could be written as:

int check (string argv)
{
    for (int i = 0, n = strlen(argv); i &lt; n; i++)
    {
        if (!isalpha(argv[i]))
        {
            return 1;
        }
    }

    return 0;
}




check() is not an informative name for a function. It should at least be check_key, or is_all_alpha or something. shift() is a similarly cryptic name.



By using strlen, we are unnecessarily iterating the string twice (once to find the null at the end, then again to process it). We can simply run until we find a null character:

for (int j = 0; plaintext[j]; ++j)




Don't use magic numbers. What are 122, 26, or 97?



Variables should be declared as close to their point of usage as possible, and initialized with real values. (key, index, new_code).



It would be better to use another variable (e.g. keyword) as an alias, rather than using argv[1] throughout the code.

There is no need to strlen() the keyword for every character.



The main loop always ends up printing a char. The rest of the code just decides what the char will be. So perhaps it could be abstracted into a function. Also, since we are expected to treat both 'a' and 'A' the same, we can simplify things by only outputting letters in lowercase.

I'd be inclined to do something like this (not tested):

string key = argv[1];
int key_len = strlen(key);
int key_i = 0;
for (int j = 0; plaintext[j]; ++j)
{
    char p = plaintext[j];
    char c = !isalpha(p) ? p : encode_char(p, key[key_i++ % key_len]);
    printf("%c", c);
}

printf("\n");
...

char encode_char(char plaintext, char key)
{
    assert(isalpha(plaintext)); // #include &lt;assert.h&gt;

    int k = shift(tolower(key));
    int p = shift(tolower(plaintext));

    return unshift((p + k) % 26);
}